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");
}
View Code

 

最终输出的值并未改变。。

地址传递

#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

 

posted on 2023-07-24 13:56  开源侠  阅读(17)  评论(0编辑  收藏  举报