C语言点滴

1,c语言指针运算
指针的运算实质是地址的运算。c语言有一套适用于指针、数组等地址运算的规则,正是这套规则赋予了C语言出色的处理能力。
对于指针指向变量,能进行基类型数据所能进行的全部运算。 1.引用运算
1)取地址运算(&)
取地址运算“&”,我们已非常熟悉。对指针变量进行取地址运算,可以得到指针变量本身的地址。
2)取内容运算(*)
取内容运算“*”,前称指针运算,用于获取地址数据对应存储单元的内容。取内容运算的优先级与取地址运算优先级相同,也为第2级,结合性亦为右结合。对指针变量,进行取内容运算可以得到指针变量所指向的数据。
取内容运算与取地址运算实质上是一对互逆运算。例如:
int a, p=&a;
*(&a)就是a,&(*p)就是p;p指向a,*p与a等价。 2.算术运算
指针变量可以进行有限的算术运算。
1)加减运算
指针变量“加上”或“减去”一个整数n,相当于指针变量加上或减去n个指针所指向数据的存储单位,即指针由当前指向位置向后或向前移动n个指针所指向数据的存储单位。
加减运算常用于数组的处理。对指向一般数据的指针,加减运算无实际意义。例如;
int a[10],*p=a,*x;
x=p+3;/*实际上是p加上3*2个字节赋给x,x指向数组的第三个分量*/
对于不同基类型的指针,指针变量“加上”或“减去”一个整数n所移动的字节数是不同的。例如:
float a[10],*p=a,*x;
p=p+3;/*实际上是p加上3*4个字节赋给x,x依然指向数组的第三个分量*/
2)自增自减运算
指针变量自增、自减运算具有上述运算的特点,但有前置后置、先用后用的考虑,务请小心。例如:
int a[10],*p=a,*x;
x=p++;/*x指向数组的第一个分量,p指向数组的第二个分量*/
x=++p;/*x、p均指向数组的第二个分量*/
*p++相当于*(p++)。*(p++)与(*p)++含义不同,前者表示地址自增,后者表示当前所指向的数据自增。
3)指针相减
指针相减得到两指针之间数据的个数,一般用于数组处理。 3.关系运算
两指针的关系运算表示两指针的先后位置关系,一般用于数组处理。除空指针外,不
能进行指针与一般数值的关系运算。 2,c语言scanf()中变量前必须要有地址运算符
3,在 C89 中,main( ) 是可以接受的。Brian W. Kernighan 和 Dennis M. Ritchie 的经典巨著 The C programming Language 2e(《C 程序设计语言第二版》)用的就是 main( )。不过在最新的 C99 标准 中,只有以下两种定义方式是正确的:
int main( void )
int main( int argc, char *argv[] )
4,*p[2]是指针数组,实质是一个数组,里面的两个元素都是指针。 []的优先级比*的优先级高,p先与[]结合,形成数组p[2],有两个元素的数组,再与*结合,表示此数组是指针类型的,每个数组元素相当于一个指针变量
指针数组:如char *str_B[5] 系统至少会分配5个连续的空间用来存储5个元素,表示str_B是一个5个元素的数组,每个元素是一个指向字符型数据的一个指针。 如果我做这样的定义: char a[3][8]={"gain","much","strong"}; char *n[3]={"gain","much","strong"}; 他们在内存的存储方式分别如右图所示,可见,系统给数组a分配了 3×8的空间,而给n分配的空间则取决于具体字符串的长度。 此外,系统分配给a的空间是连续的,而给n分配的空间则不一定连续。 由此可见,相比于比二维字符数组,指针数组有明显的优点,一是指针数组中每个元素所指的字符串不必限制在相同的字符长度,二是访问指针数组中的一个元素是用指针间接进行的,效率比下标方式要高。 但是二维字符数组却可以通过下标很方便的修改某一元素的值,而指针数组却无法这么做。 5, 指针数组,故名思义,就是指针的数组,数组的元素是指针;
数组指针,同样,就是指向数组的指针。 简单举例说明: int *p[2]; 首先声明了一个数组,数组的元素是int型的指针。
int (*p)[2]; 声明了一个指针, 指向了一个有两个int元素的数组。 其实这两种写法主要是因为运算符的优先级, 因为[]的优先级比*高。所以第一种写法,p先和[]结合,所以是一个数组,后与*结合,是指针。后一种写法同理。
指针数组如下处理就会很清楚:
typedef int* intPtr;
intPtr p[2];
一目了然,所以为了避免迷惑,做适当的typedef也是很有必要的。
同理,数组指针也可以作类似处理:
typedef int intArray2[2];
intArray2 * p;
和原来的声明都是等价的。 个人建议编程过程中采用typedef来进行类型定义,这样程序看起来会清晰很多。举个例子说明: 数组指针,元素为指向数组的指针:)
首先,指向数组的指针为:
typedef intArray2* intArray2Ptr;
然后是一个数组的元素:
typede intArray2Ptr intArray2PtrArr3[3];
最后数组的指针: intArray2PtrArr3 *p; 呵呵,写到这里自己也有点晕了,反正我工作以来从来没有写过这么拗口的程序,仅作参考,实际应用价值不大。
但是开题所提到两种情况应该是比较常见的。另外就是函数指针。另外详述。

6,标准输入scanf

输入格式控制字符串的含义
输入格式控制字符串
含义
%d
将输入转为int
%ld,%hd
将输入转为long,将输入转为short
%i
将输入转为int,与%d的不同在于可以输入八进制与十六进制
%u
将输入转为unsigned int
%o
将输入转为int,并假设输入的是八进制
%x,%X
将输入转为int,并假设输入的是十六进制
%e,%f,%g
将输入转化为float,可输入小数或带指数的数
例如:23.4e2↵

7,标准输出printf输出格式控制字符串的含义

输出格式控制字符串
含义
%d
输出为int
%ld,%hd
输出为long,输出为short
%u
输出为unsigned int
%o,%#o
输出为八进制
%x,%X,%#x,%#X
输出为十六进制,%x是abcdef,%X是ABCDEF。
%f,%e,%E
解释成浮点数,%e是用e表示指数,%E是用E。

理解C指针: 一个内存地址对应着一个值

    一个内存地址存着一个对应的值,这是比较容易理解的。

    如果程序员必须清楚地知道某块内存存着什么内容和某个内容存在哪个内存地址里了,那他们的负担可想而知。
    汇编语法对“一个内存地址存着一个对应的数”,作了简单的“抽象”:把内存地址用变量名代替了,对内存地址的取值和赋值方式不变。
    c语言对此进行了进一步的抽象:变量 <==> (一个内存地址,对应的值)(这里忽略类型等信息)。

    把C语言中的基本类型(int,long,float等),指针,数组等还原为(一个内存地址,对应的值)后,就能更清淅地理解它们了。

    内存就相当于(addr,val)的大hash表,c语句的语义基本就是改变hash值。

    为了下文的方便,特定义如下语义(遵循C的标准语义): 


    var  <==>  (addr, val)  (var为一个变量名,addr为var在内存中的首地址,val为var 的值)
    &var <==> addr
    var  <==> var作为左值出现(即等式左边)时,var等价于 addr;
               var作为右值出现(即等式左边)时,var等价于 val;
    *var <==> val



    注:符号"<==>" 右边出的等式 x = y(x是一个内存地址,y是一个值); 表示将内存地址为x的内容置为值y,如addr = 3表示置内存addr里的值为3


    现在利用上面的语义解释一下这些例子:
    int i = 3; 
    假设 i的内存地址为 0x8049320 ,那么这句话的语义是0x8049320 = 3,经过i = 3后,i为(0x8049320,3)

    int b = i;
    假设 b的内存地址为 0x8049324 ,那么这句话的语义是0x8049324 = i对应的val = 3,此时b为(0x8049324,3)

    int *p = &b
    指针p也是一个变量,int **p,int *p[8],在这些申明中p都只是一个指针变量,它和其他的变量的不同之处在于它的大小是定的,它的类型信息只是编译器用来进行类型检查和其他一些作用的(如果没有类型检查,你可以用任何的方式对一个变量进行操作如int i; ****i = 3)。假设p的地址为0x8049328,则根据p = &b的语义p.addr = b.addr,p为(0x8049328,0x8049324)

    *p = 5;
    语义为 0x8049324 = 5,此时只改变了内存地址为0x8049324的值,即改变了b的值(0x8049324,5),而p的值并未改变

    int **q = &p; //如果写为int **q = &&i; gcc编译不通过
    假设q的内存地址为0x8049330,语义为 0x8049330 = addr(p) = 0x8049328;所以q为(0x8049330, 0x8049328)
    (int **q = &&i, 要是编译过了则q应该表示为(0x8049330, x),内存地址为x的地方表示为(x,0x8049320),那么地址x为多少呢? )

    **q = 6
    语义为 val(val(q)) = val(0x8049328) = 0x8049324 = 6,将内存地址为0x8049324的内容置为6,即将b的值置为6,b为(0x8049324,6)
    
    对于结构,这些语义也适用,因为结构里的成员也是有对应地址的,也能表示为(addr,val)的形式。    
    
    对“一个内存地址存着一个对应的值”的抽象程度越高,越不用关心底层,如java。  
    Haskell已经没有副作用之说了,更不用关心这些了。


转载于:https://my.oschina.net/lao4/blog/341223

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值