目录
1. 相关资料
2. 快速入门
2.1 数据类型
void万能类型
小结
2.2 变量本质
2.3 内存“四区”
2.4 指针
一级指针
一级指针与字符串
一级指针与数组
二级指针(**)
数组、数组指针,指针数组
多维数组做函数参数 退化的本质
函数指针
小点:const 强化
小点:define宏定义
小点:typedef
小点:static强化
小点:extern关键字
1. 相关资料
- Video
- C语言基础入门视频学习:链接: https://pan.baidu.com/s/1_-5CdTPXkxtYpbVj3cv_fQ 密码:osfu (备注:视频讲的很清晰,但是时间比较长)
- 教程:
- 菜鸟教程:http://www.runoob.com/cprogramming/c-exercise-example1.html (备注:适合入门)
- C语言知识点总结:https://wenku.baidu.com/view/75569ee8f8c75fbfc77db254.html
- C语言与数据结构精讲: http://c.biancheng.net/cpp/u/shuju/
- 难点Blogs
- C语言内存管理:https://blog.csdn.net/lovemysea/article/details/79338678 (备注:注意内存的四个区域及功能)
- 指针:https://blog.csdn.net/constantin_/article/details/79575638 (备注:需要注意指针的含义,数组与指针的关系)
2. 快速入门
2.1 数据类型

void万能类型

小结

2.2 变量本质
- 程序通过变量来申请和命名内存空间 int a = 5;
- 通过变量名访问内存空间
- (一段连续)内存空间的别名(是一个门牌号)
- 修改变量有几种方法?
- 直接
- 间接。内存有地址编号,拿到地址编号也可以修改内存--->指针(编程案例2)
- 数据类型和变量的关系 ?答案:通过数据类型定义变量
- 总结及思考题
- 对内存,可读可写
- 通过变量往内存读写数据
- 不是向变量读写数据,而是向变量所代表的内存空间中写数据。

2.3 内存“四区”


2.4 指针
- 指针是一种数据类型
- 指针也是一种变量,占有内存空间,用来保存内存地址
- *p操作内存
- 间接赋值(*p)是指针存在的最大意义
- 引申: 函数调用时,用n指针(形参)改变n-1指针(实参)的值。
一级指针
典型用法:做函数参数
int showbuf(char *p)
int showArray(int *array, int iNum)
int geLen(char *pFileName, int *pfileLen);
理解:主调函数还是被调用函数分配内存;被调用函数是在heap/stack上分配内存
一级指针与字符串
//若没有指定长度,默认不分配零
char buf1[] = {'a', 'b', 'c', 'd', 'e'};
//若指定长度,不够报错;buf长度多于初始化个数,会自动补充零
char buf2[6] = {'a', 'b', 'c', 'd', 'e‘};
char buf3[6] = {'a', 'b', 'c', 'd', 'e‘};
//char buf4[5] = {'a', 'b', 'c', 'd', 'e’};
- C语言中的字符串是以’\0’结束的字符数组
- C语言中的字符串可以分配于栈空间,堆空间或者只读存储区
一级指针与数组
- 元素类型角度:数组是相同类型的变量的有序集合 测试指针变量占有内存空间大小
- 内存角度:连续的一大片内存空间
- 数组名是数组首元素的起始地址,但并不是数组的起始地址
- 通过将取地址符&作用于数组名可以得到整个数组的起始地址
- 数组名 在做函数参数时 退化成指针
二级指针(**)
典型用法(指针做函数参数)
int main(int arc ,char *arg[]); 字符串数组
int shouMatrix(int [3][4], int iLine);
int Demo64_GetTeacher(Teacher **ppTeacher);
int Demo65_GetTeacher_Free(Teacher **ppTeacher);
int getData(char **data, int *dataLen);
Int getData_Free(void *data);
Int getData_Free2(void **data); //避免野指针
理解: 主调函数还是被调用函数分配内存;被调用函数是在heap/stack上分配内存
数组、数组指针,指针数组
- 数组
- 数组指针:指向 数组的 指针 int (*a) [10]
- 指针数组:指针的数组 int *a[10]
定义数组指针的方式
typedef int (MyArrayType) [5];
MyArrayType *pointer; // a[5]
typedef int (*MyPointer) [5];
MyPointer myPoint;
int (*pointer)[n];
多维数组做函数参数 退化的本质
- 二维数组可以看做是一维数组
- 二维数组中的每个元素是一维数组
- 二维数组参数中第一维的参数可以省略
- void f(int a[5]) ---> void f(int a[]); ---> void f(int* a)
- void g(int a[3][3]) ---> void g(int a[][3]); ---> void g(int (*a)[3])
函数指针
- 一个数据变量的内存地址可以存储在相应的指针变量中,函数的首地址也以存储在某个函数指针变量中。这样,我就可以通过这个函数指针变量来调用所指向的函数了。
- 函数指针变量的声明:
void (*funP)(int) ; //声明一个指向同样参数、返回值的函数指针变量。
小点:const 强化
- 修饰普通变量 à常量
- 指针常量和常量指针
- 修饰函数参数
- 防止修改指针指向的内容: void StringCopy(char *strDestination, const char *strSource);
- 防止修改指针指向的地址: void swap ( int * const p1 , int * const p2 )
- 修饰函数的返回值:
- 如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
const char * GetString(void);
const char *str = GetString(); //right
// char * = GetString(void); wrong
小点:define宏定义
参考:https://blog.csdn.net/xingjiarong/article/details/47282255
- 关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点:
- 预编译指令只是对值进行简单的替换,不能进行类型检查
- 可以保护被修饰的东西,防止意外修改,增强程序的健壮性
- 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
- 预编译指令只是对值进行简单的替换,不能进行类型检查
#define MAXTIME 1000
#define max(x,y) (x)>(y)?(x):(y);
小点:typedef
- 定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
typedef char* PCHAR;
PCHAR pa, pb;
- 用在旧的C代码中,帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:
typedef struct tagPOINT{ int x; int y; } POINT;
POINT p1;
- 为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化
- 原声明:void (*b[10]) (void (*)());
- 简化:
- 变量名为b,先替换右边部分括号里的,pFunParam为别名一:
- typedef void (*pFunParam)();
- 再替换左边的变量b,pFunx为别名二:
- typedef void (*pFunx)(pFunParam);
- 原声明的最简化版:
- pFunx b[10];
小点:static强化
- 参考:
- https://www.cnblogs.com/wly603/archive/2012/04/11/2442065.html
- https://www.cnblogs.com/wly603/archive/2012/04/11/2442065.html
- static变量定义
- 局部
- 静态局部变量在函数内定义,生存期为整个源程序,但作用域与自动变量相同,只能在定义该变量的函数内使用。退出该函数后, 尽管该变量还继续存在,但不能使用它。
- 对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。
- 全局
- 全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。但是他们的作用域,非静态全局 变量的作用域是整个源程序(多个源文件可以共同使用); 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。
- static函数(也叫内部函数)
- 只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。区别于一般的非静态函数(外部函数)
小点:extern关键字
- extern char *ctime(const time_t *);
- 具有文件外部链接,但是声明extern变量时,编译器并不会给这个变量分配内存,在另外的文件中定义这个文件时才会为其分配内存,一旦声明了extern关键字,对编译器来意味着:
- 这个变量声明(即数据类型和变量名,但是编译器并没有分配内存)
- 这个变量的定义在其他文件中(在定义变量的文件中编译器才会为其分配内存)