9

C语言基本功修炼秘籍第5节,初学者福音,几个C语言指针的问题详解

 3 years ago
source link: https://blog.popkx.com/c%E8%AF%AD%E8%A8%80%E5%9F%BA%E6%9C%AC%E5%8A%9F%E4%BF%AE%E7%82%BC%E7%A7%98%E7%B1%8D%E7%AC%AC5%E8%8A%82-%E5%88%9D%E5%AD%A6%E8%80%85%E7%A6%8F%E9%9F%B3-%E5%87%A0%E4%B8%AAc%E8%AF%AD/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C语言基本功修炼秘籍第5节,初学者福音,几个C语言指针的问题详解

发表于 2019-07-20 17:07:23   |   已被 访问: 340 次   |   分类于:   C语言   |   暂无评论

鉴于很多C语言初学者都觉得指针非常难,本文将初学者常常觉得迷惑的地方以问答的形式解答,希望能够对读者有所帮助。

小明定义了一个C语言函数 int f(int * ),为什么 f(&5) 不能正常工作呢?

如果希望传递 5 给函数 f(),在C99中,可以使用下面这种方法:

f( (int[]){5} );

上面这行C语言代码相当于定义了一个数组,并且数组只有一个元素 5,函数 f() 接收到的参数是该数组,只不过这一过程中的数组名没有“显示”。

抛开C99的这个特性,C语言调用 f(&5) 就不能这么写了,而是需要借助变量:

int five = 5;
f(&five);

在C语言中,接受某个值指针的函数是有可能通过该指针修改该值的(即使程序员无此打算,C语言还是会一直这么认为),因此只有变量才能胜任。在C语言中,常数是只读的,只能作为右值,& 运算符是不能处理常数 5 的,f(&five) 会引发编译错误。

表达式 * p++ 增加了 p 指向的数值,还是指针 p 本身?

C语言中的 ++ 和 -- 运算符的优先级高于 * 运算符,所以 *p++ 其实就相当于 *(p++)。显然,++ 运算符增加的是指针 p 本身的值,不过在指针 p 自增之前,*p++会先返回 p 指向的值。如果希望 ++ 运算符增加 p 指向的值,应该使用括号运算符:(*p)++

小明想使用指针操作数组里的数值,下面这段C语言代码有什么问题?

小明预计程序会输出 3,但是程序却输出了“垃圾值”,他的C语言代码如下,请看:

int array[5], i, *ip;

for(i = 0; i < 5; i++)
    array[i] = i;
ip = array;

printf("%d\n", *(ip + 3 * sizeof(int)));

为什么上述C语言程序没有按照小明的预期输出?

小明是“聪明反被聪明误”了,他发现数组 array 是 int 型的,所以在指针加数上乘上了 sizeof(int)。其实,C语言中的指针加法运算会自动考虑元素类型的,所以小明应该修改 printf() 语句的代码:

printf("%d\n", *(ip + 3));

这样他的C语言程序就会输出数组 array 的第 3 个元素了。事实上,使用指针 ip 访问数组 array 中的元素时,也可以以“数组的形式”进行,例如:

printf("%d\n", ip[3]);

这样也就清楚了,小明原本的写法相当于 ip[3*sizeof(int)],如果 int 类型占用 4 个字节,其实也就是访问了 array[12],这超出了数组范围,程序的输出自然就不可预知了。

小明使用 char * 指针指向 int 型数组,为什么下面这行C语言代码就不正常了呢?

小明的C语言代码是下面这样的,他发现这段代码甚至都不能编译通过,为什么呢?

int main()
{
    int arr[] = {1, 2, 3, 4, 5};

    char *p = (char *)arr;
    ((int*)p)++;

    return 0;
}
01b9e1fde08f06f957758cc3ab65c1a4.png

这是因为在C语言中,强制类型转换运算并不意味着“忽略类型差异,强制按照指定类型执行”。按照C语言标准,强制类型转换将产生一个右值,而 ++ 运算符只能操作左值(lvalue),因此标准C语言编译器当然会报错了。例如使用gcc编译器编译上述C语言代码,将得到如下错误:
b62c73a56a31650a3c911c0fa441a965.png

可能有部分C语言编译器可能做了非标准扩展,能够编译上述代码。

小明写出((int*)p)++;的本意应该是希望产生下面这样的C语言代码:

p = (char *)((int *)p + 1);

或者以更简单的形式:

p += sizeof(int);

再或者,也可以多写一行C语言代码:

int *ip = (int *)p;
p = (char *)(ip + 1);

虽然有多种方法可以实现小明的想法,但是仍然应该尽量避免使用不恰当类型的指针操作数据。

C语言的大多语法都是简单易懂的,这常常会给初学者一种“别人都说C语言难,我看也不过如此”的感觉。但是这种感觉常常会停止在初学者学到指针时,这是一些读者跟我说的。为此,本节讨论了几个比较典型的初学者会遇到的问题,并做了较为详细的解析。

本专栏是C语言基本功修炼秘籍,对指针的剖析自然是少不了的,接下来几节,将更加深入的探讨指针的用法,敬请关注。

阅读更多:   C语言


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK