《C Primer Plus》手记一

本贴最后更新于 1607 天前,其中的信息可能已经时过境迁

0.槽

现在的书大都太啰嗦,把读书的人当作傻子。这本书也不例外。
或许详细的 API 手册才是我所需要的吧。。。

1.变量

1.1Int

//1.16位计算机 int 区间 为 -32678 - 32677所以ISO C 规定int的取值范围最小为 -32678 - 32677

// erns,dogs 并没有初始化;
int erns;
int cows = 32,goats = 14;
int dogs,cats = 69;

//打印
printf("%d",cats);
//八进制
printf("%o",0144);
// 十六进制
printf("%x",0x64);
//显示前缀
printf("%#o, %#x",0144,0x64);

1.2 修饰符

// short 短 比int范围短
// long 长 比int多
// long long 非常长 至少要64位机器比long长
// unsigned 无符号 非负数情况 unsigned int 0 ~ 65535
// signed 有符号 signed int - 32638 - 32637

long int estine;
long johns;
short int erns;
short ribs;
unsigned int s_count;
unsigned players;
unsigned long headcount;
unsigned short yesvotes;
long long ago;

// unsigned int  %u
printf("%u",null);
// short
printf("%h",null);
// long
printf("%ld",null);
// long long
printf("%lld",null);
// unsigned long
printf("%llu",null);

1.3char

实际上字符在 memory 种存储类型为 int 再经过码表转换成字符
码表:ASCII,kanji,Unicode,GBK,IEC10646
单 char 占位 1bit

char a = 'a';
chat A = 65;

非打印字符

\a 警报
\b 退格
\f 换页
\n 换行
\r 回车
\t 水平制表
\v 垂直制表
\\ 反斜杠 \
\' 单引号(转义)
\0oo 八进制值
\xhh 十六进制值

打印字符

printf("%c",'c');

1.4_Bool

true
false

1.5 浮点

float a = 3.11
double b = 3.1415926
long double c = 3.11111111111111111111111111111111111

1.6 类型大小

printf("%d",sizeof(int));
printf("%lld",sizeof(long long int));

1.7 字符串

//  char array = string
char str[40] = "ajskdfjslkafjlsadjfks";
// array size
printf("%d",strlen(str));

内存里每一个地址存一个字符,由连续的内存地址存储字符串,遇到\0 后结束,\0 也占用 char array 中的一个位置
image.png

char a = 'x';
char b[1] = "x";

1.8 常量

#inculde<stdio.h>
#define PI 3.14
int main(){
// 只读
const int i = 0;
printf("%0.2f",PI);
return 0;
}

2.流控运算符

都差不多:+ - * / % > < = ++ -- ! || & && 三元 ?:

while(true){}

for(int i = 0 ;i < 5;i++){}

do{}while(_Bool);

if(_Bool){} else{}

if(_Bool){} else if(_Bool){}

while(true){ if(xxx){ continue;} else if(yyy){break}

switch(xxx){
	case 1:
		break;
	case 2:
		break;
	default:
		break;
}

if(size >12){ goto a}else{goto b};
a:cost = cost*=1.5
b:bill = cost*=2

3.Input/Output IO

// 获取字符
getchar();
// 输出字符
putchar()
// echo
#include<stdio.h>
int main(void){
char ch;
// 如果不输入 # 那么一直读字符
while((ch = getchar()) != '#'){
	// 同时一直输出字符
	putchar(ch);
}
return 0;
}

// 无缓冲
char a = 'a'
// 将字符存入缓冲区
getchar();

完全缓冲:当缓冲区被填满时才刷新缓冲区
行缓冲:当出现换行符时刷新缓冲区,enter 刷新缓冲区

#include<stdio.h>
#include<stdlib.h>
int main()
{
int ch;
// 文件类型指针
FILE * fp;
char fname[50];
printf("Enter the name of the file");
scanf("%s",fname);
// 打开输入的文件 并用指针指向该文件,以r读权限打开
fp = fopen(fname,"r");
// 如果空指针,证明没有文件
if(fp == NULL){
	printf("no file");
}
// 循环读取文件中的每一个字符最后打印
while((ch = getc(fp))!=EOF){
	putchar(ch);
}
// 关闭文件
fclose(fp);
}

4.函数

#inculde<stdio.h>
// 书写函数前要声明,不带参数,无返回值
void say(void);
// 返回int值带两个int型参数
int sum(int a,int b);
int main(void){
say();
return 0;
}

void say(){
putchar("H");
}
int sum(int a,int b){return a+b};

5.指针

// int类型的值
int a = 0;
// &为寻址符,查找a的内存地址
printf("%p",&a);
// 声明指针b
int * b;
// 将a的地址给b
b = a;
// 格式化输出地址
printf("%p",b);

6.数组二维数组

int main(){
	int a[10] = {1,2,3,4,5,6,7,8,9,0}
	int * b;
	printf("%d",a[0]);
	// 将 a 的首元素地址给了指针b
	b = a;
	// *b 表示 首元素地址所指向的值
	printf("%d",*b);
	// *(b + 1) = a[1]
	printf("%d",*(b+1));
	// *b + 1 = a[0] + 1 = 2
	printf("%d",*b+1);	

	for(int i = 0;i < 10;i++) printf("%d",*(b+i));
}
#include<stdio.h>
#define ROWS  3
#define COLS 2

int main(void){
	int array[ROWS][COLS] = {{1,2},{3,4},{5,6}}
	for(int i = 0;i<ROWS;i++){
		for(int j = 0;j<COLS;j++){
			printf("%d",array[i][j]);
		}
	}
	// 创建一个指针,指向一个含有ROS个元素的数组
	int (*pa)[ROWS];
	printf("array[0][0] = %d",**pa);
	// *pa为第一组的地址值,+1为第一组元素的第二个元素的地址值
	printf("array[0][1] = %d",*(*pa+1));
	// pa为第一组元素整体的地址值,+1 移动到第二组元素,外层指针获取第二组第一个元素的地址又加了一个地址所以为第二组元素第二个位置的值
	printf("array[1][1] = %d ",*(*(pa+1)+1));
}


注意,指针和数据类型都具有一定的兼容性,具体规则为
长度大类型/指针 = 长度小类型/指针 比如 long a; int b = 10; a = b;
声明指针类型函数

void somefunction(int (*pt)[4]);
void somefunction(int [][4]);

PS:变长数组不是说数组的长度可变,而是声明数组时可以使用变量指定数组的维度

7.复合字面量

不用声明直接用的变量,也称不上变量。。。

// 没有声明的数组,只能直接传递进函数/表达式
(int[2]){10,20}

int sum(const in ar[],int n);

sum((int [3]){1,2,3},3);

8.字符串

c 里面没有提供字符串的实现,只有字符数组

// 第一位 a ,第二位 "\0"标志字符串结束
char a[1] = "a"
#include <stdio.h>
#define MSG "I am a symbolic string constant."
#define MAXLENGTH 81

int main(void) {
 // 声明字符串
  char words[MAXLENGTH] = "I am a string in array.";
 // 用指针直接声明字符串
  const char *p1 = "Somethings is pointing at me.";
// puts,属于stdio.h 的输出函数,只显示字符串
  puts("Here are somt things.");
  puts(MSG);
  puts(words);
  puts(p1);
  words[8] = 'p';
  puts(words);
}

指针,和数组指针一个道理

char car[10] = "Tata";
car == &car[0]
*car == 'T'
*(car+1) == car[1] == 'a'
// *pt1可以改变
const char *pt1 = "Some thing is pointing at me"
// ar1不能改变
const char ar1[] = "Some thing is pointing at me"
#include <stdio.h>
#define SLEN 40
#define LIM 5
int main(void) {
// 5个指针的数组,五个字符串存在静态内存中,指针指向他们
  const char *mytalents[LIM] = {
      "Adding numbers swiftly", "Multiplying accurately", "Stashing data",
      "Following instructions to the letter", "Understanding the C language"};
// 5个数组的数组
  char yourtalents[LIM][SLEN] = {"Walking in a straight line", "Sleeping",
                                 "Watching television", "Mailling letters",
                                 "Reading email"};

  int i;
  puts("Let's compare talents");
  printf("%-36s %-25s \n", "MyTalens", "YourTablens");
  for (i = 0; i < LIM; i++) {
    printf("%-36s %-25s \n", mytalents[i], yourtalents[i]);
  }
  //指针占了40字节而数组占了200字节
  printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd", sizeof(mytalents),
         sizeof(yourtalents));
}

copy

#include<stdio.h>

int main(void)
{
  const char * mesg = "Don't be a fool!";
  const char * copy;
  copy = mesg;

  printf("%s\n",copy);
 // 两个地址都一样,所以字符串没有被copy
 // 只是copy指向了mesg所指向的静态区域的字符串
  printf("%p\n",mesg);
  printf("%p\n",copy);
}

8.1 函数

gets();

char words[80];
//Input 一些字符读取到words里面
gets(words);
puts("%s",words);

fgets,fputs,由于 gets()不限定长度,所以可能会擦除使用的内存来存储字符串,是极为不安全的,所以出现了 fgets,fputs

char words[5];
// fgets 传入 要接受输入的字符串变量,字符串长度,输入流
fgets(words,5,stdin);
// fputs 传入 输出的字符串变量,输出流
fputs(words,stdout);

demo

#include <stdio.h>
#define STLEN 10

int main(void) {
  // 初始化
  char words[STLEN];
  int i;
  puts("Enter strings (empty line to quit):");
  // 循环读取字符
  while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
    i = 0;
    // 循环便利字符串
    while (words[i] != '\n' && words[i] != '\0') {
      i++;
    }
    // 如果 遇到换
    if (words[i] == '\n') {
      // 直接截断
      words[i] = '\0';
    } else {
      while (getchar() != '\n') {
        continue;
      }
    }
    puts(words);
  }
  puts("done");
  return 0;
}

丢弃读到的\n 换行符
gets_s()

char words[20];
gets_s(words,20);

scanf() 走连续的单词,

#include <stdio.h>

int main(void) {

  char name1[11],name2[11];
  int count;

  printf("Please enter 2 names.\n");
 // 输入过多也会导致内存溢出,加上字段宽度限定符可解决
  count = scanf("%5s %10s",name1,name2);

  printf("Iread the %d names %s and %s.\n",count,name1,name2);
}

输出函数 put(),fputs(),printf();

简而言之 put/puts 遇到空字符结束输出
fputs 针对 file 类型文件进行输出
printf 格式化输出

strlen() 获取字符串长度

void fit(char *string,unsigned int size){
	if(strlen(string)>size){
		string[size]="\0"
	}
}

strcat() 拼接字符串

char a[1] = "1"
char b[1] = "2"
strcat(a,b);

strncat()
无法检查第一个数组是否能容纳第二个数组,如果空间不够大,会溢出到相邻的存储单元

if(strlen(a) + strlen(b) <=2){
	strncat(a,b);
}

strcmp() 码表排序比较

strcmp("A","A") = 0
strcmp("A","B") = -1
strcmp("C","A") = 1
// 逐个比较至 a 和 空字符 ,码表开头为空字符,所以返回1
strcmp("appa","app") = 1

strncmp();

strncmp("abcde","abcdef",5) == 0

strcp strncp

// 把b的字符串复制到a指向的内存 含有\0
strcp(a,b);
//表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest
strncpy(char *dest, const char *src, int n)

// 查找 c 在 s 重的位置返回指针,找不到返回空指针
strchr(const chars ,char c);
// 返回 s1 字符串中 s2 字符串首次出现的位置,没有就返回空指针
strstr(const char
s1,const char*s2);

demo:字符串排序

#include <stdio.h>
#include <string.h>
// 限制字符串长度
#define SIZE 81
// 可读入最多行数
#define LIM 20
// 空字符停止输入
#define HALT ""
// 字符串排序
void stsrt(char *strings[], int n);
char *s_gets(char *st, int n);

int main(void) {
  // 存储输入数组
  char input[LIM][SIZE];
  // 内含指针变量的数组
  char *ptstr[LIM];
  // 输入计数
  int ct = 0;
  // 输出计数
  int k;

  printf("Input up to %d lines,and I will sort them.\n", LIM);
  printf("To Stop,press Enter key at a line's start.\n");
  while (ct < LIM && s_gets(input[ct], SIZE) != NULL && input[ct][0] != '\0') {
    ptstr[ct] = input[ct];
    ct++;
  }
  stsrt(ptstr,ct);
  puts("\n Here's the sorted list:\n");
  for(k =0;k<ct;k++)
  {
    puts(ptstr[k]);
  }
}

void stsrt(char *strings[], int n) {
  char *temp;
  int top, seek;

  for (top = 0; top < n - 1; top++) {
    for (seek = top + 1; seek < n; seek++) {
      if (strcmp(strings[top], strings[seek]) > 0) {
        temp = strings[top];
        strings[top] = strings[seek];
        strings[seek] = temp;
      }
    }
  }
}

char *s_gets(char *st, int n) {
  char *ret_val;
  int i = 0;
  // 获取输入
  ret_val = fgets(st, n, stdin);
  // 如果输入的不为空
  if (ret_val != NULL) {
    // 循环输入的字符,遇到\n或者\0截止
    while (st[i] != '\n' && st[i] != '\0') {
      i++;
    }
    // 如果循环到了 '\n'换行符,直接截断
    if (st[i] == '\n') {
      st[i] = '\0';
    } else {
      // 循环获取输入
      while (getchar() != '\n') {
        continue;
      }
    }
  }
  return ret_val;
}

8.2 Command Line Interface

Command 命令行 参数/参数个数/参数转换

#include <stdio.h>
#include <stdlib.h>
void argTemplate(char **argv, int);

int main(int argc, char *argv[]) {
  int count;
  printf("The command Line has %d arguments:\n", argc - 1);
  for (count = 1; count < argc; count++) {
    printf("%d:%s\n", count, argv[count]);
  }
  printf("\n");
  argTemplate(argv,argc);
  return 0;
}

void argTemplate(char **argv, int length) {
  int i, times;
// atoi 把 argv的参数转换成int 传递给times
// atof 把 字符串转换成double
// atol 把字符串转换成long
// strtol()  strtod() strtoul() 字符串转long double unsigned long 找规律吧

  if (length < 2 || (times = atoi(argv[1])) < 1) {
    printf("Usage: %s positive-number\n",argv[0]);
  }
  else{
    for(i =0 ;i<times;i++)
    {
      puts("Template String Array!");
    }
  }
}

1.C 字符串是 char 数组,内存中结尾为\n;
2.字符串常量也叫做字符串字面量;
3.函数使用指向字符串的指针,实质是指向内存字符数组的首个元素的地址;
4.fgets 获取一行输入,puts(),fputs()显示一行输出;
5.C 中有多个处理字符串的函数,在 string.h 中和 ctype.h 中
6.main 可以接受两个参数,第一个参数为参数个数,第二个参数为所有参数
7.atoi,atol,atof 把字符串转换成 int,long,double,等等

9.存储类别链接

概念:
每个值都占用一定的物理内存,C把这样一块儿内存称为对象Object
对象可以存储一个多个值,或者并未存储实际值,但是在存储时一定具有相应的大小空间
int entity = 3
entity(identifier)标识符:用来指定(designate)特定对象的内容(左值)
存储期(storage duration): 对象在内存中存了多久
作用域(scope): 标识符在哪里可以用
链接(linkage): 作用域和链接描述了程序哪部份可以使用标识符

private file scope

static int dodgers =3;
int main(){
...
}

public file scope

int giants =5;
int main(){
...
}

C 对象有四中存储期:静态存储,线程存储,自动存储,动态分配
静态存储:程序的执行期间一直存在
线程存储:从被声明时到线程结束
自动存储:块作用域里的变量

存储类别 存储期 作用域 链接 声明方式
自动 自动 块内
寄存器 自动 块内使用 register
静态外部链接 静态 文件 外部 所有函数外
静态内部链接 静态 文件 内部 所有函数外使用 static
静态无链接 静态 块内使用 static

auto:比如静态外部链接有 number,你要在块中定义 number,又不希望标识冲突,可以用 auto int number;

外部链接静态变量使用:extern,

int Errupt;
double Up[100];
extern char Coal;

当别的文件需要使用 Coal 时

int main(void){
// 声明
extern char Coal;
}
#include <stdio.h>

void report_count();
void accumulate(int);
int count = 0;
int main(void) {
  // 自动变量
  auto int value;
  // 寄存器变量
  register int i;

  while (scanf("%d", &value) == 1 && value > 0) {
    // 文件作用域变量
    ++count;
    for (i = value; i >= 0; i--) {
      accumulate(i);
    }
    report_count();
    return 0;
  }
}

void report_count(){
	printf("%d",count);
}
#include <stdio.h>

// 引用式声明,外部链接
extern int count;
//静态定义内部变量
static int total = 0;
void accumulate(int);

// int k 块级变量
void accumulate(int k) {
  // 静态内部变量无链接
  static int subtotal = 0;
  if (k < 0) {
    printf("llop cycle:%d\n", count);
    subtotal = 0;
  } else {
    subtotal += k;
    total += k;
  }
}

10.内存管理

malloc 来分配内存
free 来释放内存

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  float *ptd;
  int max;
  int number;
  int i = 0;

  puts("What is the maximum number of type double entries?");

  if (scanf("%d", &max) != 1) {
    puts("number not correctly entered --bye");
    exit(EXIT_FAILURE);
  }
  ptd = (float *)malloc(max * sizeof(float));
  if (ptd == NULL) {
    puts("Memory allocation failed.GoodBye");
    exit(EXIT_FAILURE);
  }
  puts("Enter the values(q to quire)");
  while (i < max && scanf("%1f", &ptd[i]) == 1)
    i++;
  printf("Here are your %d entries:\n", number = i);

  for (i = 0; i < number; i++) {
    printf("%7.2f", ptd[i]);
    if (i % 7 == 6) {
      putchar('\n');
    }
  }
  if (i % 7 != 0)
    putchar('\n');
  puts("Done");
  free(ptd);
  return 0;
}

calloc

long * newmen = (long*) callloc(100,sizeof(long));
int main(){
	int n;
	int *pi;
	scanf("%d",&n);
	// 分配内存 需要手动free
	pi = (int *)malloc(n*sizeof(int));
	// VAL变长数组 自动存储自动消亡
	int ar[n];
}

限定符

// 指针指向的内存地址不能变但值可以变
const float *pt; 
float const * pt;
// 指针可以变,指向的值不能变
float * const pt;
// 指针和值都不能变
const float * const pt;
// array为只读,不可改变
void display(const int array[],int limit);
// 拼接字符串的函数,第一参可变,第二参不可变
char * strcat(char * restrict s1,const char * restrict s2);
// 直接存在寄存器,快速读取
volatile int i =0;
//关键字允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问该对象唯一且初始的方式。
restrict int * pt;
// 原子化,在一个原子操作结束之前 其他的地方不允许使用该原子变量
_Atomic int hogs 

存储类别和内存管理总结

自动:在块中不带存储类别说明符/auto的变量,自动存储期,块儿级作用域,无连接
寄存器:在块中带register说明,自动存储期,块作用域,无连接,无法获取内存地址
静态,无链接:在块中带static ,静态存储期,块作用域,无链接
静态,外部链接:在所有函数外且没有static关键字,静态存储期,文件作用域,外部链接
静态,内部链接:在所有函数外使用了static,静态存储期,文件作用域,内部链接
动态分配内存:malloc()或其他函数,返回一个指向指定字节数内存块儿的指针,free释放后便可自此使用.
类型限定符号:const,volatile,restrict,_Actomic
const:限定数据在运行时不能改变
volatile:限定数据出了被当前程序修改外还可以被其他进程修改
restrict:方便编译器设置优化方案,限定指针是访问他所指向数据的唯一途径
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    83 引用 • 165 回帖 • 47 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • pleaseok

    最近在读这本书,感觉是真的特别啰嗦啊,跳着看完了...并没有什么收获

  • 其他回帖
  • someone
    作者

    我套...这是我写的吗?