C语言拾遗
C语言拾遗¶
前言:实际上我觉得这些几乎都不重要,但是发现有些记忆迷糊了,权且记录一下.
- ASCLL码中: A 65 a 97¶
- getchar() putchar() EOF = end of file = -1)
这两者可以这样搭配使用:
int c;//由于char类型并不知道是unsigned还是signed,因此用int
while((c=getchar())!=EOF)
{
putchar(c);
}
- %5d: 是右对齐,至少打印5位,如果不够5位就会在左边用空格补齐. -5d是左对齐,会在右边加上空格.
- scanf的返回值是一个整数,表示成功读取的变量个数. 因此如果返回0,那么就意味着读取到0个变量;返回-1,则意味着发生错误或者读到结尾(这时-1就是EOF)
- printf的返回值为打印的总字符个数.
- goto语句和label必须处在同一个函数里.
- 生成0-99随机数:
srand(time(0)); int num = rand()%100;
- 引用另一个源文件的全局变量:
extern int a; static int a = 0;修饰局部变量会使得变量不会反复创建;修饰全局变量会使得其为内部链接.- VS2022调试:
- 逐过程:F10
- 逐语句:F11
- 监视数字内容:arr,10
- 二维数组:arr,2,3
- 负数的补码和原码的转化都是非符号位取反,然后+1
- a^a = 0,由此得出单身狗算法:一个数组中每个数字都出现两次,但有一个只出现一次.寻找这个数字只需要全部异或一遍
- 交换的两种:
a = a+b;//可能会越界 b = a-b; a = a-b; a = a^b; b = a^b; a = a^b;//当a==b的时候,上述过程就坏了,且只能用于整数.速度也慢 - 消去n最右边的1: n = n&(n-1) 或者写成
n &= n-1每次应用,可以得到:1101 -> 1100 ->1000
- sizeof是操作符而不是函数的原因:可以写出
sizeof a - sizeof是编译期推断的,是通过类型推断的
则这里由于int赋值给s后会截断,因此n是2
short s = 10; int i = 2; int n = sizeof(s = i+4) - VS中的地址:
- 0x00 00 00 0a,0a是低地址.
- 0a(以一列显示) 00 00 00 这当中 0a是低地址
- 指针类型决定了指针+1跳过多少字节,也决定了两个同类指针相减后得到的结果是什么
- const:
const int* p = &n//const修饰*p,表示不能修改*p,即p指向的内存空间的内容. int * const p = &n//const修饰p,表示不能修改p,也就是p的指向 #define NDEBUG就是自动忽视掉assert
- 数组名就是首元素地址,除了两个例外:
- sizeof 数字名
-
&arr 这时两者都是代表整个数组
- 比如&arr + 1则会跳过整个数组
- 注意,如果func(int arr[]) { sizeof arr//这里会得到4/8,因为数组已经退化 }
- 数组指针:int (p)[10] = &arr; 这时候变量的类型就是int ()[10]
- 函数指针: int add(int x,int y);
int (pf)(int,int) = &add;
这样调用的时候pf就直接调用了函数,当然为了可读性,我们也可以写成 (pf)(2,3)
考题:
(*(void(*)()) 0)();是什么? void(*)()是函数指针类型,外面的()是强制转化,也就是把地址0强制类型转化成函数指针,然后调用 - typedef void(*pf)(int); 这样以后函数指针类型就是pf了
-
isspace判断空白字符
- islower判断是否是小写字母.是,则返回非零数,否则,返回0
- isupper isdigit tolower toupper
- strcpy (str1,str2)把str2的内容拷贝到str1中
- 拷贝是从str2的开头到str2的第一个\0
- str1长度必须大于等于str2,比str2多的部分保持不变.但是注意,str2的\0也会被拷贝进来
- strcat (str,p) 字符串追加
- 删掉str末尾的\0,加上p的内容
- 注意保证str的空间足够
- 注意自己不能给自己追加.不过可以用strncat
- strcmp (str1,str2) 按字典序比较
- 如果str1>str2,返回值大于0
- 如果str1==str2,返回值等于0
- 如果str1<str2,返回值小于0
- 以上三个函数,都有n版本.如果是strncpy,str2的前n个字符没有\0,那么也不会主动追加\0;如果str2长度小于n,那么会自动补齐\0.但是如果strncat,不论如何都会自动加\0
- strstr(str1,str2)返回str2第一次出现在str1中的pos
- erron :c语言中的一个全局变量.strerror:把erron转换成字符串.
#include<errno.h> int main() { FILE* pf = fopen("add.txt","r"); if(pf==null) { printf("fopen: %s\n",strerror(errno)); //上面这句等价于: perror("fopen"); } return 0; }
- memcpy (des,src,n) n为字节数
最好不要写出拷贝的src和des在同一个数组这种事情. - memset (arr,c,n)把从arr开始的n字节变成c
-
memcmp(p1,p2,n)比较n个字节
- 小问题:比较两个数组{0,4},{0,5},需要n为多少即可?
- 为5,因为内存中:00 00 00 00 00 01 00 00 与 00 00 00 00 11 00 00 00即可
-
不论大端还是小端,总会用4个字节中的较小地址来代表其地址.
- 小端的想法比较符合地址空间:
00 高地址 00 00 01 低地址 这里则是表示1,而不是01000000
- 常见写法:
typedef struct node { ... }node; - 结构体对齐规则
- 对齐数 = min(默认对齐数,成员变量的大小)
- 默认对齐数:VS中为8,Linux中则没有这个对齐数
- 每个成员变量都在对齐数的整数倍的地址处
- 结构体总大小为最大对齐数的整数倍
- 数组不被视为一个成员.而是把数组中存储的类型(char/int)视为成员
- 对齐的原因:减少访存次数,增加速度 因此我们需要让占用空间尽量小的成员尽量集中在一起
- 位段:
跨平台性很差.在存储时,如果一个字节可以存储多个字段,如a和b,那么a和b就会存在这个字节当中.一个字节中还有一个位没用,这个位如何处理取决于编译器. 位段没有地址,不能取地址.它实际上创造了一个比char还小的存储单位.
struct A { int _a :2;//表示a只用到2个位,如果赋值为4,那么会截断 int _b :5; int _c:10; int _c:30;//注意不要超过32 }
- 联合体: 大小即为最大成员的大小.如果最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍.
- malloc:
是会返回null的.需要进行检查.
这几句一定要有
int*p = malloc(...); if(p==null) { perror("malloc"); return 1; } free (p); p = nullptr;
- calloc (num,size)创建num* size大小的空间,同时初始化
- realloc(要调整的空间初始地址p,调整后的新内存大小);不会进行初始化
- 最好用新的指针来接受relloc,因为一旦开辟失败,会导致原先的指针p失效,发生泄露
- realloc(null,40)等于malloc(40) 以上两者都需要判空.
free同一块区域两次,会导致程序崩溃.free(null)不会出问题.
- 柔性数组 C99
- 一个数组,作为结构体的最后一个成员
- 未知大小的数组
sizeof A 不会计算柔性数组的大小.用法:malloc给其分配大小.
struct A { int i; int a[];// int a[0];也可 }struct A* p = (struct A*)malloc(sizeof(struct A) + 10*sizeof(int))扩容用realloc即可.
- 文件操作
FILE* pf = fopen("文件名","w/r/a");//write 会清空源文件 //同时需要注意:文件名中的\可能需要转移 fputc('x',pf);//向文件中写入c int ch = fgetc(pf);//从文件中读取一个字符 //循环读取 while((ch=fgetc(pf))!=EOF) { fputc(ch,stdout); } fputs("hello",pf); char arr[20] = {0}; fgets(arr,20,pf);//第二个参数是最大读取字符数 这个函数返回null表示读取结束 //注意,这个函数足够安全,如果最后读取的数据20字符里都没有\0,那么会把最后一个字符变成\0 fprintf(pf,"hello"); fscanf(pf,"%d",&a); //类似的:sprintf(s,"hhelo") 写入到s fwrite(arr,sizeof(int),7,pf)//从arr中提取7个元素,每个元素大小与int一样,写入到pf fread //相反 fseek()//移动磁盘.不说了 feof/ferror,判断读取结束的原因是否是遇见文件结尾/是否是因为错误而结束 if(feof(fp)) printf("..."); else if(ferror(fp)) printf("..."); fflush(pf);//刷新缓冲区 ...... fclose(pf); pf = null;
- 预处理:
//常见两个预处理宏 __LINE__,__FILE__ 常用 //换行符 #define DEBUG ("file: %s,\ line: %s",\ __LINE__,\ __FILE__ ) //正确定义 --参数独立,整体独立 #define MAX(a,b) ( (a)>(b)?(a):(b)) //条件编译: #if //.. #endif #if .. #elis.. #else #endif #if defined(a) #ifdef a //等于上面这个 #ifndef
如果 %d 遇见空白符,那么会跳过.跳过的数据会被从缓冲区删掉.如果遇见字母,那么不会跳过也不会取用,而是直接从缓冲区跳出.
release时,会自动删掉assert
sizeof 一般而言是编译期求值的. 但是有个例外:c99的变长数组中,如果我们sizeof 数组名,那么这个sizeof不得不在运行时求值.