指针的基本概念
指针与数组
- 指针可以指向数组元素:
int a[10],*p;
p = &a[0];
*p = 5;
- 如果==指针指向数组元素,那么对该指针进行算运算==就是有意义的:
- 指针加上整数:指针加上整数,代表该指针指向更右边数组的元素,如
int *p = &a[0]
,给p加上2之后,p指向a[2]
- 指针减去整数:指针减去整数,代表该指针指向更左边数组的元素,如
int *p = a[2]
,给p减去2之后,p指向a[0]
- 两个指针相减:两个指针相减,结果为这两个指针指向的数组元素之间间隔的数组元素个数,如
int *p = &a[5]; int *q = &a[1]
,那么p和q相减的结果就是4。
- 可以直接将数组名赋值给指针变量:
- 如:
int a[2] = {0, 1, 2}; int *p = a;
- 实质是p指向该数组的第一个元素
a[0]
- ==数组的名字实质上是指向数组第一个元素的指针==
- 不能给数组名赋值。
- 数组型形式参数:
- 在将数组作为形式参数传递给函数的时候,实际传递的是==指向该数组的第一个元素的指针(或者说是数组名)==。
- ==声明形式参数为指针就相当于声明变量为数组==(但是普通变量不可以这样理解),可以==将数组型形式参数声明为指针==,如:
int function(int a[], int n)
与int function(int *a, int n)
等价。
- 可以给函数传递==数组的片段==,如:
function(&a[6], 10)
- 用指针作为数组名:
- 可以将指针看做数组名进行==取下标操作==。
- 编译器将
p[i]
看做`*(p+i)。
- 对指针进行取下标操作的前提是:==该指针是指向了一个数组的首元素==。
- 示例:
int a[10], *p;
p = a;
int num = p[1];
- 指针数组:
- 元素是指针的数组,如:
int *p[10]
,声明了一个数组,这个数组存储10个int型指针变量。
- 指向指针的指针:
char *p1;
char **p2 = &p1;
指针和多维数组
- C语言中的多维数组,可以理解为将每一行的元素依次排列;
- 将二维数组看做是一维数组来处理:
- 指针指向二维数组的第一个元素,指针每次加一,都会指向数组的下一个元素,如果到达本行末尾,就指向下一行的第一个元素。
- 将指针指向某一行的第一个元素:
int a[10][10], *p;
//指向第二行的第一个元素
p = &a[1][0];
- 简写:根据可以对指针进行去下标操作,利用
&a[i][0] = &(*(a[i] + 0))
int a[10][10], *p;
//指向第三行的第一个元素
p = a[2];
- 指向一维数组的指针:
- 对一维数组,我们常用的指针是指向==数组的第一个元素的==;
- 可以定义==指向一个一维数组的指针==:
int (*p)[10]
,这是声明了一个指向长度为10的整型数组的指针p;
- 对于二维数组,可以看做是==一个每个元素都是一维数组的一维数组==;
- 使用指向数组的指针来==按行遍历二维数组==:
int a[10][11], (*p)[10];
//省去数组赋值等
//按列遍历数组,并将数组第五列的值赋为0
for(p = &a[0]; p < &a[10]; p++) {
(*p)[4];
}
- 在二维数组中,
a[i]
取的是第i-1行第一个元素的地址,&a[i]
取的是第i-1行的地址(第i-1个一维数组的地址)
- 对于指向数组的指针来说:
*p
是取出指向的数组,(*p)[i]
是取出指向的数组中的第i-1个元素。
- 用多维数组名作为指针:
- 二维数组的==数组名是一个指向一维数组的指针==,例如
int a[10][10]
的数组名a
代表的意思是&a[0]
。
- 多维数组的长度:
- 对于==一维数组在声明时就赋值的情况下,可以省略数组的长度==,如:
int a[] = {1, 2}
- ==二维数组在声明时就赋值可以省略第一维的长度==,最后的二维数组的每一维的长度都是每一行的数组的长度是==根据最长的数组的长度来确定==的。如果有一些行的数据不够填满整行,那么C语言==会用空字符
\0
来填补==。
字符串数组
二维数组实现字符串数组
- 字符串字面量:
- 字符串是一个==字符数组==;
- 字符串字面量就是这个数组的数组名,==是一个
char *
类型的指针==。
- 字符串字面量是==不能被修改的==
- 创建一个二维数组,按照每行一个字符串(==一维字符数组==)的方式,将一系列字符串存入一个数组中。
- 示例:
char planets[][8] = { "Mercury", "Venus", "Earth",
"Mars", "Jupiter", "Saturn",
"Urans", "Neptune", "Pluto" };
- 因为每一行字符串的长度是省略的,所以每一行的长度是系统自动确定的。在二维数组中,每一行的数组的长度是根据最长的数组的长度来确定的。如果有一些行的数据不够填满整行,那么C语言会用空字符
\0
来填补,造成了空间浪费。
- 可以理解为用这种方式创建的二维数组都是矩形的。
指针数组实现字符串数组
- 要想实现参差不齐的二维数组,需要使用元素为指针的数组。
- 建立一个元素都是指向字符串的指针的数组,来实现字符串数组。
- 示例:
char *planets[] = { "Mercury", "Venus", "Earth",
"Mars", "Jupiter", "Saturn",
"Urans", "Neptune", "Pluto"};
字符串数组的应用:命令行参数
- 运行程序是需要提供一些信息,这些信息从命令行中提供,称为命令行参数(C语言中也叫程序参数)。
- 为了访问命令行参数必须将main函数定义为含有两个特殊参数的的函数:
argc
参数:argc是参数计数的参数,为int型,用于记录命令行参数的数量。
argv
参数:argv是指向命令行参数的指针数组,这些命令行参数以字符串的形式存储。
- argv是
char *argv[]
型的,实质就是一个字符串数组。
argv[0]
指向程序名;
argv[1]
到argv[argc - 1]
指向余下的命令行参数。
argv[argc]
是一个附加元素,这个元素始终是一个空指针。
- 示例:
#include <stdio.h>
#include <string.h>
#define NUM_PLANETS 9
int main(int argc, char* argv[]) {
char *planets[] = { "Mercury", "Venus", "Earth",
"Mars", "Jupiter", "Saturn",
"Urans", "Neptune", "Pluto" };
int i, j;
for (i = 1; i < argc; i++) {
for (j = 0; j < NUM_PLANETS; j++) {
if (strcmp(argv[i], planets[j]) == 0) {
printf_s("%s is planet %d\n", argv[i], j + 1);
break;
}
if (j == NUM_PLANETS) {
printf_s("%s is not a planet\n", argv[i]);
}
}
}
return 0;
}
动态存储分配
- 动态存储分配:在程序==运行期间==分配内存单元的能力
- 内存分配函数所获得的内存块都来自一个称为**堆(heap)**的存储池。
- 内存分配函数有三种,都是声明在
<stdlib.h>
头中的:
malloc
函数:分配内存块,但是不对内存块进行初始化。(最常用,最高效)
- 原型:
void *malloc(size_t size)
,malloc函数分配size个字节的内存块,并且返回指向该内存块的指针。
- 示例:
int *a = (n * sizeof(int));
,为长度为n的int型数组分配空间。
calloc
函数:分配内存块,并且对内存块进行清零。
- 原型:
void *calloc(size_t nmemb, size_t size);
,calloc函数为nmemb个元素的数组分配内存空间,其中每个元素的长度都是size个字节。
- 在分配了内存之后,calloc函数会==通过把所有位设置为0的方式进行初始化==。
- 将nemeb设为1,可以为==任何类型的数据项分配内存==。
- 示例:
int *a = calloc(n, sizeof(int))
,为长度为n的int型数组分配空间。
realloc
函数:调整先前分配的内存块大小。
- 原型:
void *realloc(void *ptr, size_t size);
,调用realloc函数时,==ptr必须指向先前通过malloc,calloc或realloc的调用获得的内存块==。size表示内存块的新尺寸。
- 如果realloc==以空指针作为第一个实际参数,那么它的行为就行malloc函数一样==。
- 如果realloc==以0作为第二个实际参数,那么它会释放掉内存块==。
- 内存分配函数的返回值:
void *
:内存分配函数会返回void *
类型的值,该类型的指针是==通用指针==,指向分配的内存。
- 可以将==
void *
类型的变量赋给任意类型的指针变量。
- 空指针:
- 当找不到需要的足够大的内存块时,内存分配函数会返回==空指针==;
- 空指针是==不指向任何地方的指针==,这是一个用于区别于所有有效指针的特殊值。
- 测试内存分配函数的返回值是否是空指针,用来==判断内存分配是否成功==。
- NULL宏:空指针用NULL宏来表示。NULL宏在多个头(包括
<stido.h>
和<stdlib.h>
)中都有定义。
- 释放存储空间:
free()
函数:
- 原型:
void free(void *ptr)
- ==调用free函数会释放掉ptr指针指向的内存块==
- free函数的实际参数必须是==先前由内存分配函数返回的指针==。
指向函数的指针