C语言|《C Primer Plus》读书笔记
字符串和字符串函数
字符串数组初始化:
\\定义字符串数组: const char m1[40] = "Limit youtself to one line's worth."; \\可以省略数组大小,让编译器自动计算数组大小: const char m2[] = "Limit youtself to one line's worth."; \\使用指针表示法创建字符串: const char* pt1 = "Something is pointing at me.";
旧I/O函数:
gets() 读取整行输入,直到遇到换行符,然后丢弃换行符。
puts() 用于显示字符串,并在末尾添加换行符。
#include<stdio.h> #define STLEN 81 int main(void) { char words[STLEN]; puts("Enter a string,Please."); gets(words); printf("Yout string twice:\n"); printf("%s\n",words); puts(words); puts("Done."); return 0; }
新I/O函数:
fgets()第1个参数为写入的字符数组名称。
fgets()函数通过第2个参数(例如n)限制读入的字符数来解决溢出问题。
fgets()将读取n-1个字符或遇到第一个换行符为止。fgets()读到的换行会存在字符串中。
fgets()第3个函数指明要读入的文件。如果用stdin 可表示从键盘输入,stdout表示从命令行输出。
gets_s()函数不需要第三个参数,但没fgets()方便
终极版本——s_gets函数,读取整行输出并用空字符代替换行符。(或者读取一部分输出,丢弃剩余一部分。


自定义输入/输出函数:
一般可采用getchar() 和 putchar() 配合循环whlie(*str)来定制I/O函数。
//实现puts()函数 int put2(const char * string){ int count = 0; while (*string){ putchar(*string++);//获取字符并将其保存到*(string+i) count++; } putchar('\n'); return(count);//返回字符个数 }
字符串函数:
主要涉及四种字符串函数及其变种。
查询长度strlen()
//该函数可缩短字符串长度 void fit(char *string, unsigned int size){ if (strlen(string) > size) string[size] = 0; }


拼接字符串strcat()
strcat()接收两个字符串作为参数,把第二个字符串拼接在第一个字符串的末尾。
strncar()函数第三个参数指定了最大添加字符数,确保安全性(第一个字符串可能不够大来容纳第二个字符串)
strcat(str1,str2);
strncat(str1,str2,n);//n限制最大添加字符数
比较字符串strcmp()
如果两个字符串参数相同,返回0,否则返回非零值。(非零值都为真)
常用测试条件:
while(strcmp(str1,str2)){ ...... }
strncmp()第三个参数可限定查找的字符数:

复制字符串strcpy()
直接拷贝字符串是不可行的。
char ptr1[40]; char ptr2[40]; ptr1 = ptr2;//仅仅是把指针赋值
正确方法:
char ptr1[40]; char ptr2[40]; strcpy(ptr1.ptr2);//复制字符串2覆盖字符串1
strncpy(target, source, n)的第三个参数指明可拷贝的最大字符数
写入字符串sprintf()
sprintf(str1, "%s, %-19s", str2, str3); printf("%s", str1);//将"%s, %-19s"写入str1
ctype.h 字符函数
while(*str){ *str = toupper(*str);//将str字符串全部大写 str++; }
其他自搜可用。
命令行参数
main()后两个参数,需要在命令行下调用,不太会用。
实例应用:字符串排序




选择排序算法:
void stsrt(char *strings[], int num){ //采用排序指针而非字符串 char *temp; int top, seek; for(top = 0; top < num - 1; top++) //选择排序主体 for(seek = top + 1; seek < num; seek++) if(strcmp(strings[top], strings[seek]) > 0) //判断是否不同 { temp = strings[top]; strings[top] = strings[seek]; strings[seek] = temp; //交换两个字符串位置 } }

存储类别,链接和内存管理
本章难度很大,不是很理解。
部分概念:
作用域
作用域有:块作用域,函数作用域,函数原型作用域或文件作用域。
块作用域:声明在语句块中的变量,在语句块内可见
函数作用域::声明在函数中的变量,函数体内可见
函数原型作用域:仅仅在函数原型的那个括号()里面可见。
文件作用域: 变量定义在函数的外面,从定义到文件末尾可见
链接
C变量有3种链接属性:外部链接、内部链接和无链接。块作用域、函数作用域和函数原型作用域的变量都是无链接变量。
具有文件作用域的变量可以时外部链接或内部链接,外部链接可在多文件程序中使用,内部链接只能在当前单元使用。
存储类别说明符static可区分内部链接和外部链接:
int a = 6; //外部链接
static int b = 5; //内部链接
static实例:函数调用计数。该代码避免了使用全局变量,提高安全性。
1 #include <stdio.h> 2 void count(); 3 int main(void) 4 { 5 int i=0; 6 for (i = 0;i <= 5;i++) 7 { 8 count(); 9 } 10 return 0; 11 } 12 void count() 13 { 14 /*声明一个静态局部变量*/ 15 static num = 0; 16 num++; 17 printf("%d\n",num); 18 }
存储期
存储期描述了对象的生存期。(不太懂)
C对象有4种存储期:静态存储期、线程存储期、自动存储期、动态分配存储期。
静态存储期: 静态存储期的对象在程序执行期间一直存在。文件作用域变量具有静态存储期。
线程存储期:用于并发程序设计,程序执行可分为多个线程。具有线程存储期的对象,从被声明至线程结束期间存在。
以关键字_Thread_local 声明一个对象时,每个线程都获得该变量的私有备份。
自动存储期:块作用域变量通常具有自动存储期。内存自动分配和释放。 块作用域变量在前面加上static可以变长静态存储期。
随机数及实例
rand()函数
#include <stdlib.h> //前置库
#include <stdio.h> int main(){ srand(seed); //生成随机数种子 rand(); //生成随机数 }
实例:
生成1000个1-10的随机数,统计其出现次数并打印。
1 #include <stdio.h> 2 #include <time.h> 3 #include <stdlib.h> 4 #define LEN 11 5 6 void show_array(int * a, int n); 7 8 int main(void) 9 { 10 int i, a[LEN]; 11 12 for (i = 0; i < LEN; i++) 13 { 14 a[i] = 0; 15 } 16 17 for (int j = 0; j < 1000; j++){ 18 srand(j*100+(unsigned int)time(0)); //生成1000个随机数 19 for (i = 1; i < LEN; i++) 20 { 21 if(rand()%10+1 == i) //把1000个随机数放入桶中 22 a[i] +=1; 23 } 24 } 25 show_array(a, LEN); 26 return 0; 27 } 28 void show_array(int * a, int n){ 29 for (int i = 1; i < LEN; i++) 30 { 31 printf("a[%d]:%d\n",i,a[i]); 32 } 33 }
malloc()函数和free()函数
malloc()分配内存,可用来创建数组。
free()释放内存
例1:要求输入元素个数及元素值,采用malloc()函数为其设置数组并赋值
#include <stdio.h> #include <stdlib.h> int *make_array(int elem, int val); void show_array(const int ar[], int n); int main(void) { int *pa; int size; int value; printf("Enter the number of elements: "); while (scanf("%d", &size) == 1 && size > 0) //循环输入数组元素 { printf("Enter the initialization value: "); scanf("%d", &value); pa = make_array(size, value); if (pa) { show_array(pa, size); free(pa); } printf("Enter the number of elements (<1 to quit): "); } printf("Done.\n"); return 0; } int *make_array(int elem, int val){ //使用malloc()创建数组 int * ptr; ptr = (int *)malloc(elem * sizeof(int)); for(int i = 0; i < elem; i++){ *(ptr + i) = val; } return ptr; } void show_array(const int ar[], int n){ //展示数组 int cnt = 0; for(int i = 0; i < n; i++){ printf("%d ",ar[i]); cnt++; if ((cnt + 1) % 8 == 0) printf("\n"); } return; }
输出结果:
Enter the number of elements: 3 Enter the initialization value: 2 2 2 2 Enter the number of elements (<1 to quit):
例2:要求输入指定数量(n)个单词,将其存放并逐词输出。
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #define LEN 256 5 6 int main(void) 7 { 8 char **pt; //指向指针的指针,即指向数个字符串的指针 9 int i, n, length; 10 char temp[LEN]; 11 12 printf("How many words do you wish to enter? "); 13 scanf("%d", &n); 14 if ((pt = (char **)malloc(n * sizeof(char *))) != NULL) 15 { 16 printf("Enter %d words now:\n", n); 17 for (i = 0; i < n; i++) 18 { 19 scanf("%255s", temp); 20 length = strlen(temp) + 1; 21 pt[i] = (char *)malloc(length * sizeof(char)); 22 //采用malloc()存放 23 if (NULL == pt[i]) 24 { 25 printf("Memory allocation failed!\n"); 26 exit(EXIT_FAILURE); 27 } 28 strcpy(pt[i], temp); 29 //将temp内容拷贝至pt[i] 30 } 31 printf("Here are your words:\n"); 32 for (i = 0; i < n; i++) 33 { 34 puts(pt[i]); 35 free(pt[i]); 36 pt[i] = NULL; 37 } 38 free(pt); 39 pt = NULL; 40 //释放pt指向的内存后再释放pt为NULL指针 41 } 42 else 43 { 44 printf("Memory allocation failed!\n"); 45 exit(EXIT_FAILURE); 46 } 47 48 return 0; 49 }
输出结果:
How many words do you wish to enter? 3 Enter 3 words now: hello world alion Here are your words: hello world alion
文件输入输出
fopen函数
该函数声明在stdio.h中,它的第一个参数是待打开文件的名称(确切的说是一个包含该文件名的字符串地址),第二个参数是一个字符串,指定待打开文件的模式:
程序成功打开文件后,fopen()将返回文件指针file pointer,其他I/O函数可以使用这个指针指向该文件。
getc()和putc()函数
这两个函数与getchar()和putchar()类似,但是要告诉这两个函数使用哪一个文件。
1 int ch; // 用int类型的变量EOF 2 FILE * fp; 3 fp = fopen("wacky.txt", "r"); 4 ch = getc(fp); // 获取初始输入 5 while (ch != EOF) 6 { 7 putchar(ch); // 处理输入 8 ch = getc(fp);// 获取下一个输入 9 } 10 11 // 简化一下 12 int ch; 13 FILE * fp; 14 fp = fopen("wacky.txt", "r"); 15 while ((ch = getc(fp) != EOF) 16 { 17 putchar(ch); // 处理输入 18 }
fclose()函数
fclose(fp)函数关闭fp指定的文件,必要时刷新缓冲区。对于较正式的程序,应该检查是否成功关闭文件。如果成功关闭,fclose()函数返回0,否则返回EOF。
if (fclose(fp) != 0) printf("Error in closeing file %s\n", argv[1]);//argv[I]表示文件名
指向标准文件的指针

随机访问:fseek()和ftell()
有了fseek()函数,就可以把文件看成数组,在fopen()打开的文件中直接移动到任意字节处。
1 /* reverse.c -- 倒序显示文件中的内容 */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #define CNTL_Z '\032' /* DOS文本文件中的文件结尾标记 */ 5 #define SLEN 81 6 int main(void) 7 { 8 char file[SLEN]; 9 char ch; 10 FILE *fp; 11 long count, last; 12 13 puts("Enter the name of the file to be processed:"); 14 scanf("%80s", file); 15 if ((fp = fopen(file, "rb")) == NULL) 16 { 17 printf("reverse can't open %s\n", file); 18 exit(EXIR_FAILURE) 19 } 20 21 fseek(fp, 0L, SEEK_END); /* 定位到文件末尾 */ 22 last = ftell(fp); 23 for (count = 1L; cout <= last; count++) 24 { 25 fseek(fp, -count, SEEK_END); /* 回退 */ 26 ch = getc(fp); 27 if (ch != CNTL_Z && ch != '\r') /* MS-DOS文件 */ 28 putchar(ch); 29 } 30 putchar('\n'); 31 fclose(fp); 32 return 0; 33 }
fprintf()和fscanf()函数
fprintf(stdout, "%s\n", "Succ"); while (fscanf(fp, "%s", words) == 1) rewind(fp); /* 返回到文件开始处 */

浙公网安备 33010602011771号