从*p++说指针,数组,结构和函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/unix21/article/details/16116737

说明文中*p++和*s++都是一个东西,不做字面上的统一了。

因为右结合性,*p++ 其实就是 *(p++) 


1.strlen的实现

#include <stdio.h>
main(){
	char str[]= "Abcde";
	printf("\n string = %s length = %d \n",str,str_length(str));
}

int str_length (const char *s){
	int length = 0;
	while (*s++){
		length++;
	}
	return (length);
}
其实*s++的过程




递增到最后一位就是\0,也就是“”.


注意:*s已经指向后面的内容了,就是乱码了



2.数组和指针这2种方式表示字符串的差异
#include <stdio.h>
main( )
{
	char  str[]= "Abcde";
	char*  ptr = "12345";
	*ptr++;
	printf ("\n string = %s length = %d \n",ptr,str_length (ptr));
	printf ("\n string = %s length = %d \n",str,str_length (str));
}

int str_length (const char  *s)
{
	int length = 0;
	while (*s++)
	{
		length++ ;
	}
	return (length) ;
}
输出:


2.1 数组不能直接当指针用, 数组名不能指针运算
也就是说数组名代表数组的首元素,它是一个指针常量,它的值在程序运行期间是固定不变的,  *str++是不可以的;
ptr是指针变量当然可以实现ptr++的运算但是指针自然可以指针运算。


直接数组名++就报错了:


2.2 数组名不能直接赋值,但是指针可以

给数组名赋值就报错了:


第1次指针地址是:0x010f5860


第2次指针地址是:0x010f5861


第3次指针地址是:0x010f5868


第4次指针地址还是:0x010f5868


下面是《明解C语言》书的截图:


3.  int下*s++,(*s)++,++*s,++(*s)的差异
首先 如果*s是指针,那么*s++就不是将*s的值加1,而是将*s指向的地址加同一个单位大小。所以为了避免类似问题,需要的时候要加括号。

--出自《C语言入门经典 第4版》

#include <stdio.h>
void add(int *s)
{
	*s++;
}

int str_length (char  *s)
{
	int length = 0;
	while (*s++)
	{
		length++ ;
	}
	return (length) ;
}

main( )
{
	int a=1;
	int *b=&a;
	char  str[]= "Abcde";
	char*  ptr = "12345";
	*ptr++;
	ptr="def";
	add(b);
	printf ("\n string = %s length = %d \n",ptr,str_length (ptr));
	printf ("\n string = %s length = %d \n",str,str_length (str));
}


第1次*s++之前的地址是:0x002afc98


第2次*s++之前的地址是:0x002afc9c
fc9c-fc98=4  就是一个int的大小
*s++只是对s的地址递增然后解引用,得到-858993460,并没有改变*s的值!


跳出函数a没有变化


所以要想得到Int型值的递增可以这样写即可:
void add(int *s)
{
	(*s)++;
}

由于char*大小正好是1个字节,所以char*的++正好指向下一个字符
所以在字符串的操作中正好可以用*s++,如果使用了括号,(*s)++这样的话反而会引起内存错误。

4.字符串复制
#include <stdio.h>

void xstrcpy ( char  *t, char  *s ){
	while ( *s != '\0' )
	{
		*t = *s ;
		s++ ;
		t++ ;
	}
	*t = '\0' ;
}

void strcpy(char *s,char *t){
	//while((*s++=*t++)!='\0')
	//表达式通'\0'的比较是多余的,因为只需要判断表达式的值是否为0即可。
	while(*s++=*t++)
		;
}

//标准库实现
char * strcpy2(char *s,char *t){
char *r=s;
	while(*s++=*t++)
		;
	return r;
}

void main()
{
	char  source[] = "Abcde" ;
	char  target[20] ;

	strcpy ( target, source ) ;
	printf ( "\nsource string = %s", source ) ;
	printf ( "\ntarget string = %s", target ) ;
}
/*
想经过函数调用改变某个变量的值,就要传入变量的地址。
 如果变量是基本类型,要传入对应的指针类型。
 如果变量是指针,要传入指针的指针。
 */


--出自《C语言程序设计语言 第2版》



5.说清楚函数传递参数


一开始我们会想既然已经在函数中*s++了,而且也确实改变了指针的位置,但是为什么一回到调用函数中,*s就又复原了?
不是说传指针是按引用传递的么?

其实,误区也正在于此,函数还是传值的,传递的都是原值的拷贝,如果传递指针,在调用函数中使用*解引用,可以改变指针指向的值,但是不能改变指针的内容,因为函数调用只是传递了原始指针指向值地址的拷贝,就是说原始地址拷贝了2份,所以在函数中改变是改变了地址,但是没有意义。


int a = 0x55aa;

change_value(&a, 0xaa55);

这样a的值改变了,真正的情况是参数传递后,编译器做了一个参数拷贝过程,比如声明一个整型指针pCopy = &a;

这样,pCopy只是传进来参数&a的一份拷贝,但是他们都指向了a的地址,因此用这种方法可以改变a 的值。


那么现在有个问题,通过传递一个指针,可以改变该指针所指向地址的内容,那么如何改变指针本身呢?
该怎么做才能改变ptr呢?

既然ptr也是一个变量,我要在函数内部改变它,那么就传递一个指向ptr的指针!也就是二级指针!



6.函数返回指针

绝不返回函数中本地变量的地址


函数返回指针可以用如下方法:

1.可以用malloc分配内存,并返回这个内存的地址

2.返回指针参数


注意函数中可以声明一个新的结构struct,然后返回结构,但是不可以返回临时指针



7.数组和指针的差异
数组不是指针,作为参数传递的数组会退化为指针。





为什么C语言把数组形参当做指针?



再来看看《你必须知道的495个C语言问题》



8.数组不能被赋值
数组不能赋值,可以指定数组下标赋值,可以在函数中可以给数组赋值,因为这个时候数组退化为指针。




9.数组的大小




10.指针和结构

结构体指针4个字节结构体大小要看数据对齐
分配12个字节,结构吃掉8个,当 (结构地址+1)所以只能第一个元素可以分配到初始化的0,第二个元素就分到垃圾地址了

struct tt{
	int size;
	int slabs;
};
void main()
{
	void *s=malloc(12);
	struct tt *t1;
	memset(s,0,12);
	t1=(struct tt*)s;
	t1->size=1;
	t1->slabs=2;
}

指针不对齐8a88  和8a90差2   8a90和8a98差8    结构有2个Int  所以结构大小是8

结构和指针的关系
对同一个地址可以转换为任意结构体,虽然数据结构乱了,但是毕竟也是有数据。
所以使用法则也就是使用前先寻址,再强制转换


11.变量作用域

局部变量的作用域一般认为是在函数体内。但是根据C99标准,该说法有了变化。在新的标准中,允许即时定义局部变量,示例如下:

for( int i = 0; i < MAXSIZE; i++ )

{

….

}

例子中的局部变量i的作用域即在for循环的花括号中,当for循环结束的时候,局部变量i的生存周期同时结束。也就是说,在下一个for循环中,你仍然可以再次重新定义并使用名为i的局部变量。该语法只能在C99之后的新的C编译器中使用, 例如VC2005、VC2008、gcc4.2及以上版本。但是,该语法带来了编程风格的变化,而且变量隐含在了执行程序中,无论是代码的阅读和维护都有较大的困难,因此工程项目中不建议使用该语法。


for循环中定义的pit 在离开循环以后就不存在了。


12.传递结构和数组给函数的差异

为什么传递给函数的结构没有像数组一样退化为指针, 因为数组可以用指针去偏移量,但是结构无法做到这一点,或者说非常困难。

其实 函数参数默认是通过r0,r1,r2,r3四个寄存器传递的,多余的参数是通过将参数压入栈中传递的。同理对于一个结构体如果其大小少于32字节(4个寄存器),按照正常的方式通过r0到r3传递,多于32字节则是将多余的部分在堆栈上建立个备份进行传递。

    所以对于在ADS编译器下对于函数参数是结构体的传递,还是传入指针比较好。既高效(省去建立备份的时间),又节省栈空间。


13.指针大小

32位是4个字节,32=4*8     

64位是8个字节,64=8*8     

在64位的linux下:

而visual studio里默认是win32平台,指针是4个字节大小:


需要修改为x64平台

这时候显示指针是8个字节:



14.指向数组的指针
slabclass_t *p = &slabclass[id];


猜你喜欢

转载自blog.csdn.net/unix21/article/details/16116737