C语言指针的常见问题
1
值传递
下面看一个列子,student结构体中包含该学生的各种信息,我们在change函数中对其进行部分修改,再在主函数中输出其结果
#include<stdio.h> #include<string.h> #define format "%d\n%s\n%f\n%f\n%f\n" struct student { int num; char name[20]; float score[3]; }; void change( struct student stu ); int main() { struct student stu; stu.num = 12345; strcpy(stu.name, "Tom"); stu.score[0] = 67.5; stu.score[1] = 89; stu.score[2] = 78.6; change(stu); printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]); printf("\n"); return 0; } void change(struct student stu) { stu.score[0] = 100; strcpy(stu.name, "jerry"); }
最终输出的值并未改变。。
地址传递
#include<stdio.h> #define format "%d\n%s\n%f\n%f\n%f\n" struct student { int num; char name[20]; float score[3]; }; void change( struct student* stu ); int main() { struct student stu; stu.num = 12345; strcpy(stu.name, "Tom"); stu.score[0] = 67.5; stu.score[1] = 89; stu.score[2] = 78.6; change(&stu); printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]); printf("\n"); return 0; } void change(struct student* p) { p->score[0] = 100; strcpy(p->name, "jerry"); }
可以看到,通过地址传递修改了结构体内的数据
用&stu做实参,&stu是结构体变量stu的地址。在调用函数时将该地址传送给形参p(p是指针变量)。这样p就指向stu。
在change函数中改变结构体内成员的值,在主函数中就输出了改变后的值
2
使用 sum()函数。该程序打印原始数 组的大小和表示该数组的函数形参的大小(如果你的编译器不支持用转换说 明%zd打印sizeof返回值,可以用%u或%lu来代替)。
// sum_arr1.c -- 数组元素之和
// 如果编译器不支持 %zd,用 %u 或 %lu 替换它
#include <stdio.h>
#define SIZE 10
int sum(int ar[], int n);
int main(void)
{
int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26, 31, 20 };
long answer;
answer = sum(marbles, SIZE);
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes.\n",
sizeof marbles);
return 0;
}
int sum(int ar[], int n) // 这个数组的大小是?
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
该程序的输出如下:
The size of ar is 8 bytes.
The total number of marbles is 190.
The size of marbles is 40 bytes.
注意,marbles的大小是40字节。这没问题,因为marbles内含10个int类 型的值,每个值占4字节,所以整个marbles的大小是40字节。但是,ar才8字 节。这是因为ar并不是数组本身,它是一个指向 marbles 数组首元素的指 针。我们的系统中用 8 字节储存地址,所以指针变量的大小是 8字节(其他 系统中地址的大小可能不是8字节)。简而言之,在程序清单10.10中, marbles是一个数组, ar是一个指向marbles数组首元素的指针,利用C中数组 和指针的特殊关系,可以用数组表示法来表示指针ar。
3
传递地址会导致一些问题。C 通常都按值传递数据,因为这样做可以保 证数据的完整性。如果函数使用的是原始数据的副本,就不会意外修改原始 数据。但是,处理数组的函数通常都需要使用原始数据,因此这样的函数可 以修改原数组。有时,这正是我们需要的。例如,下面的函数给数组的每个 元素都加上一个相同的值:
void add_to(double ar[], int n, double val)
{
int i;
for (i = 0; i < n; i++)
ar[i] += val;
}
因此,调用该函数后,prices数组中的每个元素的值都增加了2.5:
add_to(prices, 100, 2.50);
该函数修改了数组中的数据。之所以可以这样做,是因为函数通过指针 直接使用了原始数据。
ANSI C提供 了一种预防手段。如果函数的意图不是修改数组中的数据内容,那么在函数 原型和函数定义中声明形式参数时应使用关键字const。例如,sum()函数的 原型和定义如下:
int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
return total;
}
以上代码中的const告诉编译器,该函数不能修改ar指向的数组中的内 容。如果在函数中不小心使用类似ar[i]++的表达式,编译器会捕获这个错 误,并生成一条错误信息。
4
在C语言中,当你传递一个指针给一个函数,函数接收的是这个指针的副本。所以,如果你在函数内部将这个副本置空,原始指针(在函数外部的)并不会受到影响。
#include <stdio.h> void setNull(int* ptr) { // 将指针指向的地址设为 NULL ptr = NULL; } int main() { int x = 123; int* p = &x; printf("p points to %d\n", *p); // 输出 123 printf("p points : %p\n", p); // 输出 setNull(p); // 置空指针 printf("p points : %p\n", p); // 输出 0,即 NULL printf("p still exists\n"); // 输出 p still exists return 0; }
输出结果
p points to 123 p points : 0x7fff7900dfbc p points : 0x7fff7900dfbc p still exists
浙公网安备 33010602011771号