c语言程序设计知识点总结03
地址(Address):
计算机的内存由若干个字节内存单元构成,每个字节内存单元都有一个唯一的地址用于区分和存取单元中的数据。
形式上,地址是一个无符号整数,从0开始,依次递增,在表达和交流时,通常把地址写成十六进制数。
指针(Pointer):
一个变量,它存有另外一个变量的地址。
指针相关的两个基本运算符:
1. &:取变量的地址。
2. * :取指针所指的变量的内容。
指针的定义及初始化:
语法格式: 所指变量类型 * 指针名;
例:(1) int x, *p = &x; /* 定义了一个指向整型变量x的指针p*/
(2) int x,*p;
p = &x; /* 同上 */
指针相关的表达式:
假设有:int x, *p = &x; 则:
p = &x *p = x *&x = *p = x &*p = &x = p
*p++ = *(p++) :取p所指的变量,然后p指向下一个单元。
(*p)++ :将p所指的变量加1。
两种特殊的指针:
1. void *:指向空类型的指针
指向void类型的指针可以容纳任何其它类型的指针,但容纳前必须强制转换为指向void类型的指针。
例如假设有:int x,*p;void *p2;则:
p2 = (void *)p;正确 而p2 = p; 错误。
2. NULL:空指针或零指针
由系统定义:#define NULL 0
int *p = NULL; /* 表示现在指针p什么也不指向 */
指针的运算:
1. 赋值运算:指向同类型变量的指针可以相互赋值。
2. 加减小整数:向后或向前移动整数对应的元素个数。
3. 指针相减:结果为两个指针间元素的个数。
4. 指针比较:谁在前谁在后(所指的变量的地址)。
二级指针:
指针所指的变量又是一个指针变量。如:
int x, *p1, **p2;
p1 = &x; /* p1指向x */
p2 = &p1; /* p2指向p1 */
指针与一维数组:
int a[5] = {1, 2, 3, 4, 5};
int *p = a; /* 等价于 int *p = &a[0]; */
则有数组元素a[i]的三种引用形式:
a[i] = *(a + i) = *(p + i) /* 即a+i和p+i都等于&a[i] */
注意:
1. 指针+1的含义是指针指向下一个数组元素。
2. 指针-1的含义是指针指向前一个数组元素。
3. 指针可以加减,但数组名是常量地址,不能加减。
即:p++,p-- 合法,但a++,a-- 不合法。
指针与二维数组:
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
则有数组元素a[i][j]的三种引用形式:
a[i][j] = *(a[i] + j) = *(*(a+i) + j)
/* a + i 指向第 i 行,a[i] 指向第 i 行的第一个元素a[i][0] */
注意:
1. a[0], a[1]各是一个一维数组,a[0] = {1, 2, 3}而a[1] = {4, 5, 6}。
2. 由于数组名是指针,所以a[0], a[1]分别指向二维数组的第一行
和第二行的首元素,即a[0] = &a[0][0],a[1] = &a[1][0]。
3. 数组a是由两个一级指针构成的数组,其元素分别为a[0]和a[1]。
指针数组:
数组元素是指针的数组。如:
char *str[3] = {“a1”, “a2”, “a3”};
注意:
1. str为有3个元素的一维数组。
2. 每个元素都为一个指针,即数组元素 str[0], str[1],
str[3]均为 char *类型的指针。
指向数组的指针:
int a[2][3] = {1, 2, 3, 4, 5, 6};
int (*p)[3]; /* p指向一个数组,该数组有三个整型元素 */
p = a; /* 则(*p)[0] = a[0][0], (*p)[1] = a[0][1] */
p = a + 1; /* 则(*p)[0] = a[1][0], (*p)[1] = a[1][1] */
注意:
1. 二维数组名本质是一个指向数组的指针。
2. a + i 指向二维数组的第 i 行,称为行指针。
内存分配:
1. 程序执行时必须加载到内存。无论是代码还是数据都必须在内存中获得存储单元。
2. 代码加载到称为代码段的内存区域。
3. 全局变量、静态变量和字符串常量加载到称为数据段的内存区域。
4. 局部变量在执行到相应的函数代码块时才在栈上分配内存,函数退出时则释放该内存。
5. 程序在运行时还可以向操作系统动态申请获得内存,操作系统负责从它管理的堆内存中分配一内存块,并返回内存块的首地址。当程序用完该动态申请的内存块后,可以向操作系统申请,由操作系统负责释放并回收该内存块。
注意:
1,2,3,4由编译程序和装入程序负责分配内存,而5由程序向操作系统负责申请和释放内存。
动态内存分配涉及到的库函数:
void *malloc(unsigned int size);
成功:返回所开辟空间首地址
失败:返回空指针 功能:向系统申请size字节的堆存储空间
void *calloc(unsigned int num, unsigned int size);
成功:返回所开辟空间首地址
失败:返回空指针 功能:按类型申请num个size大小的堆空间
void free(void *p);
无返回值 功能:释放p指向的堆空间
void *realloc(void *p,unsigned int size);
成功:返回新开辟空间首地址
失败:返回空指针 功能:将p指向堆空间变为size大小
动态内存分配涉及到的库函数使用说明:
1.由内存分配函数返回的是void *类型的指针,使用时必须先转换为
自己需要的类型。如:
double *pd = NULL;
pd = (double *) calloc(10, sizeof(double));
2. 申请的内存块大小一般由sizeof运算符自动计算,以适应不同的编译器,从而增加程序的可移植性。
3. 由于内存分配不一定成功(如内存耗尽 ),所以程序中一定要判断是否申请成功,成功后才能使用该指针。
if(p == NULL)
{/* 或者 if ( !p ) */
printf(“内存耗尽!”);
exit(1);
}
4. 程序使用完动态申请的内存块后一定要释放,否则会造成内存泄露,即程序运行一次,内存就丢掉一块(没有回收再利用)。
函数调用过程:
函数调用时,在执行流程转至被调函数的代码执行之前,必须先完成将实在参数传递给形式参数的过程。这个过程完成后,再顺序执行被调函数的代码,直到遇到return语句返回或者代码执行完毕返回到被调用处继续执行(除非程序异常退出)。
函数参数传递的两种方式:
1. 传值:将实参的值传给形式参数,此后形参实参两者各不相干,对形参的任何改变不影响实参。
2. 传地址:将实参的地址传给形式参数,此后形参实参两者合二为一,对形参的任何改变其实就是改变实参。
指针作为函数参数:实质是传地址。
数组作为函数参数:传数组的首地址,而不是把每个数组元素传过去。
命令行参数的概念:
程序在执行时,由命令行传递给程序的参数。实质是操作系统将命令行参数组织成一个字符串数组,然后将它传递给main函数。程序中可对该字符串数组进行处理。命令行参数就是main函数的参数。
main(int argc, char *argv[ ])
程序执行:程序名 字符串1 字符串2 ... 字符串n
↑ ↑ ↑ ↑
argv[0] argv[1] argv[2] … argv[n+1]
argc = n + 1,此处共有n + 1个命令行参数。
/*
* 第一个参数int argc:指明命令行参数的个数。
* 第二个参数char *argv[ ]:一个数组,每个数组元素为传过来的一个命令行参数(字符串)。也可写为:char **argv 。
*/
返回指针的函数:
该函数的返回类型是指针类型,即函数返回一个指针。
例: int *pi = NULL;
pi = (int *) malloc(sizeof(int));
指向函数的指针:
语法格式:
函数返回类型 (*函数指针名)(函数参数)
例:定义时:
int sum(int i, int j) { … }
int (*p)(int a, int b);
p = sum;
调用时: (*p)(3, 5) 就等价于sum(3, 5)。
标签:知识点,指向,03,int,void,内存,数组,程序设计,指针
From: https://www.cnblogs.com/Mr-black-7/p/17419704.html