你必须知道的495个C语言问题,学习体会四
本文,我们来学习下指针,这是个梦魇啊。无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出,
这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限度的发挥它的优势,而发挥C语言优势的正是指针
造物者的错
int i =;
int*p =&i;
*p =;
以上简单的代码,很不幸的混乱了指针与指针的解引用,但是 ,这是最正宗的C语言语法,我初学C指针的时候,经常把* 当作指针的代名词,所以很当然的把*出现的地方当作 指针,所以我分不清上面代码的差异,所以我可能会写出像
int*p;
*p = malloc();
这样的代码,因此,请记住,指针变量在这里就是p,而不是*p,当需要操作指针指向的内存时,才需要使用*来作为间接操作符。
这样的误导其实是C语言语法的眼球错误,因为,我们经常把以上代码写成这样:
char*p =(char*)malloc();
而这样写是完全正确的。
字符串?指针?数组?
char*p1 ="hello world";
char*p2 ="hello world";
char p3[]="hello world";
char p4[]={'h','e','l','l','o',' ','w','o','r','l','d'};
额,少有人 弄懂了 以上三者的差异:怪只怪老是有人告诉我们 数组和指针没多大区别,怪只怪C语言里字符串这个东西 太难弄清晰,怪只怪我们大多数时候都不知道字符串到底是怎么结束的,'\0'是个什么东西,怪只怪,C语言里很多字符串操作api都是不安全的。这样一来,我们估计要一辈子和C字符串打仗了,标准的C字符串实在不是个东西啊。
发了一顿牢骚,我们还是回到代码本身,
printf("p1 %s p2\n",p1 == p2 ?"equal":"not equal");
printf("p1 %s p3\n",p1 == p3 ?"equal":"not equal");
输出什么呢?
p1 equal p2
p1 not equal p3
这说明 p1 与p2 相等,p1与p3不等,这是为什么?
我们知道,"hello world"作为字符串是一个字面值常量,其存储在常量区,只有一份实体,所以凡是指向其位置的指针都是固定的值。而p3指代的是一个局部的字符数组,p3作为数组名称,其表示的是一个数组的首地址,而p1或者p2实际上是指针变量,这个指针变量就是字符常量的地址,所以p1和p2的值是一样的,而p3与二者不同就不足为怪了。
而p4就不值得一提了,它很明确的告诉你,这是一个数组,但是和p3比起来,它又少了 一个'\0',所以,通过sizeof计算,p3 为12,而p4仅仅是11,这是p3与p4的最大不同,但是最大的相同是,他们都是数组(首地址)。
最后,来总结下,指针和数组的区别:
数组是一个由(同一类型)连续元素组成的预先分配的内存块,指针是一个对任何位置的(特定类型的)数据元素的引用。
数组自动分配空间,但是不能重分配或者改变大小,指针必须被赋值以指向分配的空间(可能使用malloc),但是可以随意重新赋值(即指向不同的对象)。同时除了表示一个内存块的基地址外,还有许多其他用途。比如,可以作为动态分配的数组的首地址,对多个相似变量的一般访问,函数传参,遍历数组等。
话分两头,指针与数组所谓的等价,使用malloc分配的内存块的指针通常可以被当作一个数组,也可以使用[ ]来引用。但是 你懂的,当使用sizeof时,二者还是有差别的,不然,怎么说C语言让人烦呢,什么话都是 编剧说好的。
以上,看完 如果 你更糊涂了,请出门左转 ,把数组和指针当 不同的东西吧。
函数参数
指针作为参数参数 ,本质上是在(模拟)引用传参,这在C++中发扬光大了,现在在C中,我们姑且这么认为,比如当我们需要一个数组作为函数参数时,传它的首地址,需要传一个结构体时,传它的地址,如此,都可以当作传的数组或者结构体的引用。因此,可以说,引入引用这个思想概念,我们可以很好的清楚函数参数的 指针命题:
- 数组指针参数
事实上 不需要数组指针这么个东西,我们直接将一维数组的首地址传给函数,就可以了
#include<stdio.h>
#include<stdlib.h>
void printTest(int a[],int lenght)
{
for(int i =; i< lenght ;++i)
printf("%d ",a[i]);
printf("\n");
}
int main()
{
int a[]={,};
printTest(a,);
return0;
}
可以这么说,作为函数参数,a就是a[2]数组的 引用名,所以,操作的就是a[2]数组本身。换用常规的理解,一个数组的数组名是该数组的地址,所以在函数参数中,直接退化为指针了,虽然,我的函数参数就是a[] ,但是其本质应该是int *a。
再看一段:
二维数组的例子,希望你不要骂娘,或者觉得我有病
#include<stdio.h>
#include<stdlib.h>
void printTest1(char(*a)[],int lenght)
{
int i =;
for(;i < lenght;++i)
printf("%s\n",*(a+i));
printf("\n");
}
void printTest2(char*a[],int lenght)
{
int i =;
for(;i<lenght;i++)
printf("%s\n",*(a+i));
printf("\n");
}
int main(int argc,char* argv[])
{
char a1[][]={"hello","world"};
char*a2[]={"hello2","world2"};
char(*p1)[]= a1;
char** p2 = a2;
printTest1(p1,);
printTest2(p2,);
return0;
}
试试使用printTest2 打印p1或者相反,你就应该明白为何我这么多次一举了。
代码中a1与a2的区别,如果你懂了,那本文就达到它的目的了。这正说明了数组与指针的天囊之别。
未完待续..
你必须知道的495个C语言问题,学习体会四的更多相关文章
- 你必须知道的495个C语言问题,学习体会一
C语言作为一门古老的语言,其灵活性和容易出错都让人 又爱又恨,书籍<你必须知道的495个C语言问题>,使用问答的形式,告诉读者 C语言使用的各个方面的知识,包括一些冷知识等.以下,我要摘录 ...
- C语言学习书籍推荐《你必须知道的495个C语言问题》
萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...
- 《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...
- 你必须知道的495个C语言问题,学习体会三
本文是 本系列的第三篇,本文主要对C语言的表达式做个小结 先从两个坑爹的表达式说起:i++ 与++i 上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住 ...
- 你必须知道的495个C语言问题,学习体会二
这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...
- 你必须知道的495个c语言问题(笔记)
1.1我该如何决定使用哪种整数类型? 用到较大的数用long:空间很重要(例如有很大的数组或很多的结构)用short:此外用int. win32: int 32bit 4byte char 8b ...
- 《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数
一.ANSI C标准 1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#).“符号粘贴”操作符(##).#pragma指令. 2. Q:char a[3] = "a ...
- 《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
一.字符和字符串 1. Q:为什么strcat(string, '!')不行? A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")." ...
- 《你必须知道的495个C语言问题》读书笔记之第4-7章:指针
1. Q:为什么我不能对void *指针进行算术运算? A:因为编译器不知道所值对象的大小,而指针的算法运算总是基于所指对象的大小的. 2. Q:C语言可以“按引用传参”吗? A:不可以.严格来说,C ...
随机推荐
- 常用的Issue解决方案(EF框架)
1.提交出错:ObjectStateManager 中已存在具有同一键的对象. ObjectStateManager 无法跟踪具有相同键的多个对象. 遇到此问题,首先要确定的是主键是否赋值,以及 ...
- JavaScript:学习笔记(7)——VAR、LET、CONST三种变量声明的区别
JavaScript:学习笔记(7)——VAR.LET.CONST三种变量声明的区别 ES2015(ES6)带来了许多闪亮的新功能,自2017年以来,许多JavaScript开发人员已经熟悉并开始使用 ...
- 11 Spring框架 SpringDAO的JdbcTemplate
上几个章节我们探讨了Spring的IoC和AOP,这是Spring的重点,但是Spring对jdbc的支持同样我们也不能忘记,毕竟我们还要通过Spring来管理DAO框架(例如Hibernate或者M ...
- shell sed 命令
1:行首空格 sed 's/^[ \t]*//g' 2:行末空格 sed 's/[ \t]*$//g' 3,删除行首的空格或TAB,并删除<tr>.cat poem2id.txt | ...
- 125. Valid Palindrome(判断忽略标点的字符串是否回文,加个正则,与上一题解法一样)
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...
- CAS单点登录的配置
先说单点登录是个啥? 单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个,无须多次登录. 配置的步骤如下: 1.生成安全证书 Cas serve ...
- Django学习笔记之Django Form表单
Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否 ...
- [CTSC2008]祭祀
题目描述 在遥远的东方,有一个神秘的民族,自称Y族.他们世代居住在水面上,奉龙王为神.每逢重大庆典, Y族都会在水面上举办盛大的祭祀活动.我们可以把Y族居住地水系看成一个由岔口和河道组成的网络.每条河 ...
- 20145219 《Java程序设计》第06周学习总结
20145219 <Java程序设计>第06周学习总结 教材学习内容总结 InputStream与OutputStream 串流设计 1.串流:Java将输入/输出抽象化为串流,数据有来源 ...
- 差看windows上进程及线程
转自:http://blog.csdn.net/swgsunhj/article/details/29552027 下载process exlporer: http://technet.microso ...