学习C的一些笔记(三)

来源:互联网 发布:it就业培训班 编辑:程序博客网 时间:2024/06/11 20:34
1.表示一块内存一般采用:内存首地址+内存长度
2. linux下给某个内存初始化为0,可以使用bzero。头文件是#include<string.h>(mac 0409)
3.内存初始化的方法:1.char str[20] = “” 2.memset(str, ‘\0’, sizeof(str))/memset(str, 0, sizeof(str));
4.当输入为字符串时,scanf返回值要么是1,要么是-1,不可能为0
5.注意:数组容易访问越界,如果scanf输入越界,程序能正常运行,但退出时报错,在编译时,提示不安全。(在linux下 gets编译时,提示不安全)
6.scanf只能输没有空格的字符串,输入有空格的字符串,只能输出空格前的。
7.gets是输入一行,直到遇到enter键,停止。返回值是存放字符串的首地址,也就是个数组名,错误时的返回值为空(NULL),而不是 EOF
8.fgets能检查数组是否越界,格式:fgets(str, 1024, stdin)。注意:fgets会把换行符“\n”读入数组,以保证不越界(即,若数组长度1024,输入的长度只能是1023),而gets不会。(mac 0409 test)
9. fputs(str, stdout),打印字符串,stdout是指输出到什么地方去
10.strlen的返回值是无符号整数
11.给字符串赋值要用strcpy(str2, str1)(str2的长度要大于等于str1的长度)或者strncpy(str2, str1, n),而不能用等号。只能用于字符串赋值,数组赋值用memcpy
12.strcpy与strncpy的区别
strcpy是将str1全部赋值到str2中,而strncpy则可以指定str1的长度,将其部分赋值到str2中。
13.比较字符串大小用,strcmp(str1, str2)。原理是比较两个字符串中第一个不相等的字符的ascii码大小,返回值是int类型,若返回值等于0说明相等,若返回值大于0,说明str1>str2,若小于0说明str1<str2
14.字符串“加法”用strcat(str1, str2)相当于把str2追加到str1的后面
(前提是str1要有足够空间)
15.%s与%c区别,当要输出整个字符串时用,%s如:printf(“%s”, str); ,当输出字符串每个元素时用%c,如:for(i = 0; i < n ; i++) printf(“%c”, str[i]);(mac 0409 test)
16.显示耗时,是用clock(),具体,如下,bg = clock(); end = clock(); printf("time: %.9fs\n", (double)(end - bg) / CLOCKS_PER_SEC);(除以CLOCKS_PER_SEC是将结果转化为秒)
17.static 如果修饰函数,说明函数只能用在本文件中
18.extern是声明,声明和定义的区别就是,声明的时候不会分配内存,定义时候会分配内存。
19.头文件中不能定义变量
20.宏定义中一定要加括号,如#define SQRT(m) m*m   ……a = 4; b = SQRT(a + 1);正常结果应该为16,可是宏定义是原样替换,则是b = a + 1 * a + 1; 结果为9,所以宏定义要加括号应为:#define SQRT(m) (m) * (m)
21.int arr[10][3],为二维数组,意为,每个元素还包含3个整数的二维数组。注:数组名是数组中第一个元素的首地址。该数组中第一个元素为数组,所以数组名是数组的地址。即一个指向数组的指针。
22.int arr1[10], int arr2[10][3],arr1的类型为int *, arr2的类型为int (*p)[3](*代表指针,即有三个元素,每个元素是整型的数组) 
23.二维数组在内存中实际上为一个一维数组,内存中数组首地址为也为第一个元素的地址,如:int arr2[2][3] ={{1,2,3}, {4,5,6}};  arr2[0]表示二维数组中第一个数组地址(数组名即为地址),arr2[0][0]表示二维数组中第一个数组的第一个元素,即5,arr2代表二维数组的地址,值于arr[0]一样,但是意义不一样arr2等价于 &arr[0], arr[0]等价于&arr2[0][0]
24.arr[i][j] ->*(*(arr + i) + j) ->i * 3 + j
25.二维数组一般写成矩阵形式,如:
int arr[2][3]={\
                          1,2,3,\
                          4,5,6,\
                      }
这样写的原因是因为数组中的元素都是挨着的。
26.char *str2[10],为指针数组,数组中的每个元素都是指针,作用是动态分配二维数组,注:还有一种是数组指针,指的是这个指针指向一个数组。
27.int (*p)[10] 为数组指针,即指向数组的指针要带括号,是因为下标操作符的优先级比截引用的优先级要高。不带括号就变成数组了
28.二维数组是地址,但是不是地址的地址
29.二维数组传参数,第二维不能省,代表数组中每个元素长度,如:void array_show(int arr[][3], int len) => void array_show(int (*arr)[3], int len)
30.字符串打印时只需要知道首地址就行了
31.fgets与scanf区别,scanf输入空格,如:hello world 相当于两个字符串,而如果是用fgets,则相当于一个字符串
32.任何指针都能转成void *
33.qsort (void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) ) ),对buf指向的数据进行快速排序,如果函数compare的第一个参数小雨第二个参数,返回负值,等于,返回0,大于返回正值(一般是对数组排序),其中*compare为函数指针
34.int *arr[10]的类型为int **
35.能放字符串的数组有两类,一类是二维数组,一类是字符串指针数组(char **p)
(char *arr[10],里面是一些常数,不能修改)
36.指针也有两个属性,一个是本身,一个是指向的类型,指针是一个变量,变量里面有个地址,地址对应内存里放什么类型
37.把一个函数定义成指针跟这个函数的返回值,参数表有关系
38.判断两个函数类型是否一样,就看两个函数的返回值跟参数表的值是否一样。
39.C中函数全部都是传值
40.*如果没有加上数据类型,则代表截引用,如果加上数据类型,代表指针,指向一个数据类型。
41.int* p1, p2;其中p1的类型为int*, p2的类型为int
42.找出指针的类型的方法是:把指针申明语句中的指针名字去掉,剩下的部分就是指针的类型。如:int**ptr,指针类型为int**。int*(*ptr)[4],指针类型为int*(*)[4]
43.指针指向的类型:只需要把指针申明语句中的指针名字,以及指针申明符*去掉,剩下部分就是指针所指向的类型。如:int**ptr,指针指向的类型为:int*。int*(*ptr)[4],指针指向的类型为int*()[4]。
44.指针的值(指针指针所指向的内存区或地址):指针所指向的内存区就 是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。一个指针的值是 XX,就相当于说该指针指 向了以 XX 为首地址的一片内存区域;一个指针指向了某块内存区域, 就相当于说该指针的值是这块内存区域的首地址
45.指针和地址是等价的。
46.指针具有4要素:指针的类型,指针所指向的类型,指针指向的内存区, 指针自身占据的内存。
47.指针加法是移动一个元素, p + 1 <=> p + 1 * sizeof(*p) 其中*p代表 p指向的内容的类型的大小,如:p指向int,*p就代表是个整数,整数的长度就是4。(int)((int*)0 + 4) =  16
48.指针减法,不是两个指针的值相减,是两个指针的值相减后再除以指针指向的类型,即((int)pe - (int)pb )/sizeof(*pb)
49.访问一个数组中的元素有两种方法,1.下标法,如a[i], 2.指针法,如*(a + i)
或者*(p + i)。
50.用数组名作参数时候,会退化为指针。
51.二维数组可以转化为指针,如int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
a[1][1] => int *(*(a + 1) + 1), 其中 *(a + 1)是用来访问{5, 6, 7, 8}这个数组的,
*(*(a + 1) + 1)才是用来访问,6这个元素的
52.char str[] = “hello world”与char *str = “hello world” 不一样, 后一个hello world是一个常量,不能修改,存放在文字常量区,文字由指针指向这个常量
53.如果参数里有char* 大部分都是指字符串
54.“0123456789abcdef”[10] =>10[“0123456789abcdef”]
55.对于char* 数组(或指针数组 int *arr[])一般采用动态分配内存,这样就可以当成一个二维数组用
56.指针可以指向一个函数,函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。如:int fun1(char *, int);int (*pfun1)(char *, int); pfun1 = fun1; int a = (*pfun)(“abcdefg”, 7); //可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。用在,函数的实现者跟函数的调用者不一样(如;程序员定义一个函数,需要系统给你调用)例: signal函数,void ( *signal( int signal, void (* func) (int)) ) (int);(这是个返回值是void 参数是int的函数指针)。向系统注册一个信号处理函数,当系统(程序)接收到信号后,系统按void (* func) (int))这种方式处理(该方式为我们自己定义的)
57.char *func()为指针函数,返回值为一个指针,char (*pfunc)()为函数指针
58. 结构体是定义一种数据类型,要先定义数据类型,再定义变量
59.typedef struct tag_stu_t stu_t ; struct tag_stu_t{}; =>> typedef struct tag_stu_t{}stu_t;
60.结构体数据初始化,可以在定义时候直接用 “={}”,但一般更多的是使用“.跟->”
61.结构体中的变量在内存中不是紧挨着的,需要考虑对齐(跟编译器相关,按4对齐,不够补空格)
62.定义一个结构体数组,stu_t stu_arr[10] ,数组中每个元素都是一个结构体
63.UNIX/LINUX下,当栈空间(默认值全为cc)超过10MB会段错误,windows下是4MB(为栈溢出)栈空间系统自动回收,堆空间需要手动回收,若不释放,则会造成内存泄露
64.申请内存函数(堆空间,默认值全为cd)有三个,一个是malloc()(arr = (int*)malloc(1024))(返回一个连续空间的首地址,因为malloc返回值是void *所以可以转换为任意的类型,若分配失败,返回值为空),一个是calloc(arr  = (int*)calloc
(SIZE, sizeof(int)))(分配时按某一个单元来分,如之前是按int来分,SIZE个int,总长度为,SIZE*4),一个是realloc,(为重新分配再分配,即如果之前分配的内存不够,则继续往后接一块),其中malloc与calloc区别为,malloc需要手动初始化(memset(arr,0, sizeof(arr))),而calloc自动初始化
65.定义数组时必须知道长度
66.链表节点
 typedef struct node{
            int  nd_val;
            struct node *nd_next;(记录下一个节点位置)
}node_t, *pnode_t; node_t是给struct node起了个别名,*pnode_t是给struct node* 起了个别名 等价于 typedef struct node node_t;typedef struct  node* pnode_t; 。
链表有两个指针,一个是头指针phead,一个是用来记录链表中最后一个节点的尾指针ptail。通过头指针便可以遍历整个链表
67.尾插法,要判断该节点是否为第一次插入,如果为第一次插入,则ptail=pnew若果不是第一次插入ptail->next = pnew; ptail = pnew。每次插入完后都要修改头指针
68.释放链表用free,先保存要释放节点的next的值,再释放当前节点
while(pcur)
{
    ptmp = pcur->nd_next;
    free(pcur);
    pcur = ptmp;
}
phead = NULL;
ptail = NULL;
69.只要是涉及到修改头指针的都要传地址
70.static 静态变量(函数), 指 只在申明下的本文件中有效,不能被其他的使用。其他地方可以定义相同的变量(函数)名,而不会冲突
71.枚举是一种较新概念,格式为 enum 枚举名{枚举元素,枚举元素……},其中后一个元素是前一元素的+1值,当然也可以用户给其赋值,一般第一个元素都需要用户赋值
72.一个由C/C++编译的程序占用的内存分为以下几部分:
栈区——由编译器自动分配释放,存放函数的参数值。局部变量的值等。其操作方式类似于数据结构中的栈
堆区——一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表
全局\静态区——全局变量和静态变量存储是放一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
文字常量区——常量字符串就是存放在这里的。程序结束后由系统释放。
程序代码区——存放函数体(类的成员函数、全局函数)的二进制代码
如:指针本身是在栈区,而指针所指向的数据是在堆区.
73.栈区是由高地址向低地址扩展的数据结构,堆区是由低地址向高地址扩展 
74.fopen与freopen的区别
fopen
头文件:<stdio.h>
函数原型:FILE * fopen(const char * path, const char * mode);
path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
"r"或"rb"        以只读方式打开文件,该文件必须存在。
"w"或"wb"     以写方式打开文件,并把文件长度截短为零。
"a"或"ab"      以写方式打开文件,新内容追加在文件尾。
"r+"或"rb+"或"r+b"       以更新方式打开(读和写)
"w+"或"wb+"或"w+b"   以更新方式打开,并把文件长度截短为零。
"a+"或"ab+"或"a+b"     以更新方式打开,新内容追加在文件尾。
字母b表示文件时一个二进制文件而不是文本文件。(linux下不区分二进制文件和文本文件)
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中。

freopen
头文件:<stdio.h>
函数原型:FILE * freopen ( const char * filename, const char * mode, FILE * stream );
参数:
filename: 要打开的文件名
mode: 文件打开的模式,和fopen中的模式(r/w)相同
stream: 文件指针,通常使用标准流文件(stdin/stdout/stderr)
返回值:如果成功则返回该指向该stream的指针,否则为NULL。
作用:用于重定向输入输出流的函数,将stream中的标准输入、输出、错误或者文件流重定向为filename文件中的内容。linux下需要重定向输出很容易使用 ./程序名 >test (>>test 追加),windows下的输入输出重定向可以使用freopen。
使用方法: 因为文件指针使用的是标准流文件,因此我们可以不定义文件指针。
我们使用freopen()函数以只读方式r(read)打开输入文件test.in ,freopen("test.in", "r", stdin);
这样程序的输入就会从标准输入流stdin转换到从文件"test.in"中输入
然后使用freopen()函数以写入方式w(write)打开输出文件test.out,freopen("test.out", "w", stdout);
程序的输出就会从原来的标准输出变成写入文件"test.out"中
原创粉丝点击