指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为
int *ip;/* 一个整型的指针 */
double *dp;/* 一个 double 型的指针 */
float *fp;/* 一个浮点型的指针 */
char *ch;/* 一个字符型的指针 */
指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-
#include
void main() {
int arr[3] = { 10, 100, 1000 }, i = 0;
int* ptr = arr;
for (i = 0; i < 3; i++) {
printf("数组%d的地址为:%p\n", i, ptr);
printf("数组%d的内容为:%d\n", i, *ptr);
ptr++;//在int类型中,基本单位为4byte,因此ptr++实际上是+4,指向下一个数组
}
}
因为数组是在内存空间中是连续的,因此对指向数组空间的指针变量进行++(或者--)操作的时候,实际上是+(或者-)一个指针类型的单位大小的byte(例如:int为4byte,double为8byte),因此,它就会指向下一个(或者上一个)数组元素
#include
void main() {
int arr[3] = { 10, 100, 1000 }, i = 0;
int* ptr = &arr[2];
for (i = 2; i >= 0; i--) {
printf("数组%d的地址为:%p\n", i, ptr);
printf("数组%d的内容为:%d\n", i, *ptr);
ptr--;//在int类型中,基本单位为4byte,因此ptr--实际上是-4,指向上一个数组元素
}
}
#include
void main() {
int arr[3] = { 10, 100, 1000 }, i = 0;
int* ptr = arr;
ptr += 1;//在int类型中,基本单位为4byte,因此ptr--实际上是-4,指向上一个数组元素
printf("数组%d的地址为:%p\n", i, ptr);
printf("数组%d的内容为:%d\n", i, *ptr);
}
指针+(或-)操作,与指针++(或--)操作同理,只是它更能快速的定位到你想要的数组元素
#include
void main() {
int arr[3] = { 10, 100, 1000 }, i = 0;
int* ptr = &arr[2];
ptr -= 1;//在int类型中,基本单位为4byte,因此ptr--实际上是-4,指向上一个数组元素
printf("数组%d的地址为:%p\n", i, ptr);
printf("数组%d的内容为:%d\n", i, *ptr);
}
指针可以用关系运算符进行比较,如 ==、<、 <= 和 > 、>=。如果 p1 和 p2 指向两个变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较
#include
void main() {
int i = 0, arr[3] = { 10, 100, 100 };
int* ptr = arr;
//判断指针变量ptr内容,即其所指向的内存空间的地址
//是否小于等于数组arr中第二个元素的地址
//如果是,则进行printf
while (ptr <= &arr[1]) {
printf("arr[%d]的地址为:%p\tarr[%d]的内容为%d\n", i, ptr, i, *ptr);
ptr++;
i++;
}
}
要让数组的元素指向 int 或其他数据类型的地址(指针)。可以使用指针数组
数据类型 *指针数组名[大小];
#include
void main() {
int var[3] = { 10, 100, 1000 };
int i;
int* ptr[3];
for (i = 0; i < 3; i++) {
ptr[i] = &var[i];//将var数组元素的地址逐一赋值给相应下标的ptr指针数组
}
for (i = 0; i < 3; i++) {
printf("var[%d] = %d\n", i, *ptr[i]);
}
}
请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名), 并通过遍历该指针数组,显示字符串信息 (即:定义一个指针数组,该数组的每个元素,指向的是一个字符串)
#include
void main() {
char* books[4] = { "三国演义", "西游记", "红楼梦", "水浒传" };
int i;
for (i = 0; i < 4; i++) {
printf("\nbooks[%d] 指向字符串是=%s %p", i, books[i], &books[i]);
}
}
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置
设图中的Variable的Value为int类型,图中pointer1指向的是Variable,应该申明为 int* pointer1,则pointer2应该申明为int** pointer2
int **ptr; // ptr 的类型是 int **
当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符, 比如 **ptr(当一个目标值被一个指针间接指向时,访问这个值的指针前面需要加两个*)
#include
int main() {
int var;
int* ptr; //一级指针
int** pptr; //二级指针
int*** ppptr; // 三级指针
var = 3000;
ptr = &var; // var 变量的地址赋给 ptr
pptr = &ptr;// 表示将 ptr 存放的地址,赋给 pptr
ppptr = &pptr; // 表示将 pptr 存放的地址,赋给 ppptr
printf("var 的地址 = %p\nvar = %d \n", &var, var);// 0x1133 3000
printf("\nptr 的本身的地址 = %p\nptr 存放的地址 = %p\n*ptr = %d \n", &ptr, ptr, *ptr);
printf("\npptr 本身地址 = %p\npptr 存放的地址 = %p\n**pptr = %d\n", &pptr, pptr, **pptr);
printf("\nppptr 本身地址 = %p\nppptr 存放的地址 = %p\n***pptr = %d\n", &ppptr, ppptr, ***ppptr);
return 0;
}
当函数的形参类型是指针类型时,是使用该函数时,需要传递指针,或者地址,或者数组给该形参
#include
void test(int* p) {
*p += 1;
}
void main() {
int num = 90;
int* p = #
test(&num);
printf("num = %d\n", num);
test(p);
printf("num = %d", num);
}
#include
double getAverage(int* arr, int size) {
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i) {
// arr[0] = arr + 0
// arr[1] = arr + 1 个 int 字节(4)
// arr[2] = arr + 2 个 int 字节(8) //...
sum += arr[i];// arr[0] =>数组第一个元素的地址 arr[1]
printf("\narr 存放的地址=%p ", arr);
avg = (double)sum / size;
return avg;
}
}
double getAverage2(int* arr, int size) {
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i) {
sum += *arr;
printf("\narr 存放的地址=%p ", arr);
arr++;// 指针的++运算, 会对 arr 存放的地址做修改
}
avg = (double)sum / size;
return avg;
}
int main() {
/* 带有 5 个元素的整型数组 */
int balance[5] = { 1000, 2, 3, 17, 50 };
double avg;
/* 传递一个指向数组的指针作为参数 */
avg = getAverage(balance, 5);
/* 输出返回值 */
printf("Average value is: %f\n", avg);
return 0;
}
C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数
请编写一个函数 strlong(),返回两个字符串中较长的一个
//请编写一个函数 strlong(),返回两个字符串中较长的一个
#include
#include
char* strlong(char* str1, char* str2) {
//通过strlen函数计算字符串的长度
printf("第一个字符串的长度为%d,第二个字符串的长度为%d\n", strlen(str1), strlen(str2));
if (strlen(str1) >= strlen(str2)) {//如果字符串1的长度大于等于字符串2的长度,则返回指向字符串1的指针
return str1;
}
else {
return str2;//else,则返回指向字符串2的指针
}
}
int main() {
char str1[20], str2[20], * str;
printf("请输入一个字符串:\n");
gets_s(str1, sizeof(str1));
printf("请输入一个字符串:\n");
gets_s(str2, sizeof(str2));
str = strlong(str1, str2);//用str字符指针指向strlong返回的指针
printf("这两个字符串中,更长的那个字符串是:%s", str);
return 0;
}
#include
int* func() {
int n = 100;//局部变量, 在 func 返回时,就会销毁
return &n;
}
int main() {
int* p = func(); //func 返回指针
int n;
n = *p;
printf("\nvalue = %d\n", n);
return 0;
}
函数运行结束后,仅仅是放弃了对这块内存空间的使用权限,但是,这块内存空间的内容并没有被销毁,输出这块内存空间的内容,仍然有可能是原来的内容,但是,如果在执行多条语句后,这块内存空间就有可能被别的数据给覆盖,因此,返回的指针不能指向这些数据
C 语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static 变量
#include
int* func() {
static int n = 100; // 如果这个局部变量是 static 性质的,那么 n 存放数据的空间在静态数据区
return &n;
}
int main() {
int* p = func(); //func 返回指针
int n;
n = *p;
printf("\nvalue = %d\n", n);
return 0;
}
通过static int n =100;将100存入了静态存储区,返回的是静态存储区的地址,因此,在之后的main函数的使用中,仍然可以使用静态存储区中存储的100,而静态存储区不会被其他语句所占用
returnType (*pointerName)(param list);
用函数指针来实现对函数的调用, 返回两个整数中的最大值
#include
//说明 //1. max 函数 //2. 接收两个 int ,返回较大数
int max(int a, int b){
return a>b ? a : b;
}
int main() {
int x, y, maxVal;
//说明 函数指针
//1. 函数指针的名字 pmax
//2. int 表示 该函数指针指向的函数是返回 int 类型
//3. (int, int) 表示 该函数指针指向的函数形参是接收两个 int
//4. 在定义函数指针时,也可以写上形参名 int (*pmax)(int x, int y) = max;
int (*pmax)(int, int) = max;
printf("Input two numbers:\n");
scanf_s("%d %d", &x, &y);
// (*pmax)(x, y) 通过函数指针去调用 函数max
maxVal = (*pmax)(x, y);//可以把这个语句中的*去掉
printf("Max value: %d pmax = %p 本身的地址=%p\n", maxVal, pmax , &pmax);
return 0;
}
使用回调函数的方式,给一个整型数组 int arr[10] 赋 10 个随机数
#include
#include
// 回调函数
// //1.int (*f)(void)
//2. f 就是 函数指针 , 它可以接收的函数是 (返回 int ,没有形参的函数)
//3. f 在这里被 initArray 调用,充当了回调函数角色
void initArray(int* array, int arraySize, int (*f)(void)) {
int i;
//循环 10
for ( i=0; i
头文件 #include
函数原型 void* malloc(usigned int size) //memory allocation 取m和alloc字母组成。
作用——在内存的动态存储区(堆区)中分配一个长度为 size 的连续空间;
形参 size 的类型为无符号整型,函数返回值是所分配区域的第一个字节的地址(首地址),即此函数是一个指针型函数, 返回的指针指向该分配域的开头位置
malloc(100);//开辟100 字节的临时空间,返回值为其第一个字节的地址
函数原型 void* calloc(unsigned n,unsigned size)
作用——在内存的动态存储区中分配 n 个长度为 size 的连续空间,这个空间一般比较大,足以保存一个数组;函数返回指向所分配域的起始位置的指针;
分配不成功,返回NULL。
p = calloc(50, 4); //开辟 50*4 个字节临时空间,把起始地址分配给指针变量 p
函数原型:void free(void *p)
作用——释放变量 p 所指向的动态空间,使这部分空间能重新被其他变量使用
p 是最近一次调用 calloc 或malloc 函数时的函数返回值
free 函数无返回值
free(p); // 释放 p 所指向的已分配的动态空间
函数原型 void *realloc(void *p,unsigned int size)
作用——重新分配malloc 或 calloc 函数获得的动态空间大小,将 p 指向的动态空间大小改变为 size,p 的值不变,分配失败返回NULL
可以增大或者减小已经分配malloc或者calloc获得的空间大小
realloc(p, 50); // 将 p 所指向的已分配的动态空间 改为 50 字节
返回类型说明
C 99标准把以上malloc、calloc、realloc函数的基类型定为void类型。这种指针称为无类型指针(typeless pointer),即不指向哪一种具体的类型数据。只表示用来指向一个抽象的类型的数据,即仅提供一个纯地址,而不能指向任何具体的对象。(例如:与指向整型变量的指针相区别,整型指针提供一个地址,该地址指向一个整形变量;而无类型指针只提供地址)
c99允许使用基类型为void的指针类型。可以定义一个基类型为void的指针变量(即void*型变量),它不指向任何类型的数据。请注意:不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型"或“不指向确定的类型”的数据。在将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。(void*类型指针不能用*p的方式取值)例如:
#include
void main() {
int a = 3;//定义a为整型变量
int* p1 = &a;//定义一个指向int类型的指针变量p1,并指向int型变量
char* p2;//定义一个指向char类型的指针变量p2
void* p3; //定义一个无类型的指针变量p3(也可以理解为类型为void型)
p3 = (void*)p1;//将p1的值转换为void*类型,然后赋值给p3(可以理解为强制类型转换)
p2 = (char*)p3;//将p3的值转换换cha*类型,然后赋值给p2
printf("%d", *p1);//合法,输出a的值
p3 = &a;//只有在C99下才能这么写,相当于p3 = (void*)&a
printf("%d", *p3);//不合法,p3是无指向的,不能指向a
}
动态创建数组,输入 5 个学生的成绩,另外一个函数检测成绩低于 60 分的,输出不合格的成绩
#include
#include
void check(int *p) {
int i;
printf("\n不及格的成绩有:");
for (i = 0; i < 5; i++) {
if (p[i] < 60) {
printf("\n%d", p[i]);
}
}
}
void main() {
int* p, i;
//在堆区开辟一个5 * 4的空间,并将地址(void*),转成(int*),赋给p
p = (int*)malloc(5 * sizeof(int));
for (i = 0; i < 5; i++) {
scanf_s("%d", p + i);
}
check(p);
free(p);//销毁p的空间
}