数组与指针

论数组名与指针的关系
数组名是一个常量指针,是指针就会有类型。
指针的类型决定 指针作加减的时候,移动的步长。

数组名可以看作是指向数组第一个元素类型的常量指针。
数组名在数值上为第一个元素的地址(首地址)。

int a[10];

	数组名a ,a可以当作指针来用。如果a当作是指针来用的时候
	a<==>&a[0]

typeof(&a[0])
	&a[0] xx的地址,==>是一个指针
	
	typeof(&a[0])==> 指向类型 * ==>typeof(a[0]) *
	
	int *p;
	
	typeof(p)==>int *
	
例子:
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	
	要定义一个指针p来保存a[0]的地址 
	
	int *p;
	p=&a[0];
	p=a;
	
		p=a;=>a=&a[0]
		
		//p=&a[0];
		p=a;//此处数组名a,当作一个指针来看 
		
		printf("%p\n",&a[0]);
		printf("%p\n",p);
		printf("%p\n",a);
			//以上三个打印出来都一样 都是数组的首地址
		printf("%p\n",a[0]);//1
		printf("%p\n",*p);//1
		printf("%p\n",*(a+0));//1
		printf("%p\n",p[0]);//1
		
	---------------------------------------------
		typeof(&x) => typeof(x) *
		
			&x =>是一个指针
				指针的类型如何来描述?
					指向的类型 *
					
			&x => 是一个指针,并且保存了x的地址,所以
				&x 指向x 
					typeof(x) *
	_________________________________________________
		&x[y]+i ==> &x[y+i]
		
		int a[10];
		&a[0] 元素a[0]的地址 
		
		&a[0]+1 ==> &a[1]
		
		&a[0]+2 ==> &a[2]
		
		&a[2]+1 ==> &a[3]
		---------
		数组名a,在代码中有两个意思:
		1)数组名可以代表整个数组
			&a 
			sizeof(a)
			typeof(a)
			
		2)在合适的情况下,数组名可以当作是指针来看 
		&a[0]
		合适?根据上下文 
		a+1 
		p=a 
		
		---------
	练习:
	1.int a[10]={1,2,3,4,5,6,7,8,9,10};
		printf("a=%p,a+1=%p &a+1=%p\n",
		a,a+1,&a+1);
		&a[0]
		&a[1]
		&a[1] 反正不是这个
		
		a:当作是指针来看
			&a[0]
		a+1:当做指针来看
			&a[0]+1=>&a[1]
		&a+1: typeof(&a+1)==>指针
			typeof(&a)==>typeof(a) * =>int[10] *
			typeof(&a) 是一个指向10个int的数组的指针。
						数组指针:指向一个数组
		 &a+1==>往后挪到10个int 
		
	2.int b[3][4]={1,2,3,4,
					5,6,7,8,
					9,10,11,12};
		printf("b=%p b+1=%p &b[0]+1=%p &b+1=%p\n",
			b,b+1,&b[0]+1,&b+1);
		&b[0][0]             
		&b[1][1] &b[1][0]
		&b[1][1] &b[1][0]
		&b[3][4] &b[3][0]
		&b[0][0] &b[1]或&b[1][0] &b[1] 指向下一个数组
		
	3.分析如下程序的输出结果?
	int a[5]={1,2,3,4,5};
	int *ptr=(int*)(&a+1);
	
	printf("%d %d\n",*(a+1),*(ptr-1));//2 5
	a+1 a数组名 当成指针 =>&a[0]+1 typeof(a[0])* int* =>&a[1]
	*(a+1)==>*&a[1] a[1]
	&a tyepof(&a) => int[5] * 指向了下一个数组
	(int*)强制转换  指向的对象是int型
	*(ptr-1)=>*&a[4] =>a[4] 5
	
	-----------------------------------
	int a[5]={1,2,3,4,5};
	int *ptr=(int*)&a+1;
	
	printf("%d %d\n",*(a+1),*(ptr-1));//2 1
	&a=>int[5]* 
	(int*) 指向的对象 int +1 &a[1] 
	*(ptr-1)=>*(&a[1]-1) *(&a[0]) a[0] 		
	周:2 5
		2 1
	杨:2 5
		2 1
		
	4.有int b[3][4];假如定义一个指针变量p,来保存b[0][0]的地址,
	改如何定义?
	罗 唐 周
	 int *p;
	 p=b;//error
	 typeof(p)=>int *
	 b在此处当一个指针来看 &b[0]
	 typeof(&b[0])=>typeof(b[0])* =>int[4] *
	
	 p=b[0]  &b[0][0]
	 
	 女生:夏慧
	 p=&b[0][0]
	 
	 typeof(&b[0][0])==>tyepof(b[0][0])* int *
	 
8.多维数组与指针
	1)数组名可以看作是指向第一个元素类型的常量指针,
	并且在数值上为第一个元素的地址。
		数组名a if把a当指针
		a==>&a[0]
		
	2)C语言所有的数组都是一维数组!
		
		假如有:
			int a[3][4];
			
			    表达式的含义                表达式的值
		a       1)代表整个数组
				2)当作指针 
					&a[0]                    第0行的地址
		a[0]   a[0]又是一个数组(一维数组)
				1)
				2)当作指针 
					&a[0][0]  
		&a[0][0] 元素a[0][0]的地址 
				tyepof(&a[0][0]) => typeof(a[0][0]) *
				=>int *
		=============================
		a+1     此处a只能当指针 
				=>&a[0]+1
				=>&a[1]
		&a[1]
		
		&a      a代表的是整个数组
				&a绝对 100%是一个指针
				tyepof(&a) ==>typeof(a) *
				int[4][3] * ==> int b[10] typeof(b) ==> int[10] *
														int[4][3] *
		&a+1    int[4][3] *+1
				整个数组a的下一个地址 
		============================
		a[1]+2  a[1]数组名,此处当指针
				==>&a[1][0]+2   typeof(a[1][0])==>int 
				==>&a[1][2]
		
		*(a+1)+2 a[1]+2
				*(&a[0]+1)+2
				*(&a[1])+2
				a[1]+2
				&a[1][2]
		
		&a[1][2]
		===============================
		*(a[1]+2) a[1][2]
		
		*(*(a+1)+2) a[1][2]
		==> p = a
		*(*(p+i)+j) <==> p[i][j]
					
		===============================
		
	9.指针数组 与 数组指针
		1)指针数组 
			指针数组是一个数组,只不过它里面的元素是指针。
			
			定义数组:
				数组元素的类型 数组名[元素的个数];
			
			“数组元素的类型”:任意的合法的C类型都可以
					基本类型:如char int double float...
					构造类型:
						数组 
						结构体
						联合体
						枚举
					指针类型:
						
		如:
			int* p[4];
				p数组,里面有4个元素,每个元素的类型都是int*
				
		2)数组指针  
			数组指针是一个指针,指向一个数组 
			如:
				int a[4];
				定义一个指针p,来指向数组a(保存数组a的地址)
				p=&a;
					typeof(&a)->typeof(a) *=>int[4] *
				int[4] *p;
				==>int (*p)[4];
				
		区别:
			---------------------------------
			int a[10];
			int (*p)[10]=&a;
			*((*p)+i)<==>*(a+i)=>a[i]
			*p=>*(&a)=>a
			typeof(*((*p)+i)) <==> int 
			数组指针 *((*p)+i)
			
			int *p[10];
			*(*(p+i))<==> *p[i] ==>p[i]这个指针指向的对象
			p+i=>&p[i]
			*(p+i)=>*&p[i]=>p[i]
			typeof(*(*(p+i))) <==> int 
			指针数组 每一个元素都是指针 在这里这个里面的元素指向对象是int类型
			指针数组 *(*(p+i)) 访问数组里面的元素所指向的对象
			---------------------------------	
	 
	10.字符串与指针
		字符串是什么?
			字符串是一串字符。
			C语言中,没有字符串这个类型。
			C语言的字符串通过 char*(字符型指针)来实现的。
			C语言的字符串,使用""(双引号)引起来的一串字符来表示的,并且字符串
			后面默认会加一个\0,\0(ASCII为0的那个字符)字符串结束的标示。
			"abc"
			
			我们只需要保存字符串的首地址就可以了,从首地址开始找第一个\0,前面的
			这些字符就是字符串的里面的字符。
			
			C语言中的字符串(如:“SSSSSSSABC”)是保存在一个.rodata(只读数据段)的
			内存区域,字符串代表是这个内存空间的首地址。
			如:
			"12345"
			在程序运行的时候,系统会为这个字符串在.rodata中分配6个字节的空间给它。
			表达式“12345”的值就是,就是首字符的地址。
			
			typeof("12345")==>&'1'
			=>typeof('1') *==> const char *  
			
			char *p="123456";
			
				p保存的是字符串首字符的地址 '1'的地址 
				p+1 '2'的地址 
				
				*(p+1) ==> *&'2'==> '2'
				
				printf("%c\n",*p);//'1'
				printf("%c\n",*(p+1));//'2'
				
				*(p+1)='B';//error,p指向的字符串,是在.rodata中 
				//.rodata中的内容,不能被修改!!!!!
				
		
			字符数组
				char s[5]={'a','b','c','d','e'};
				//字符数组,与普通的数组是一样的,保存在一个.data/栈空间
				//数组区域是可读可写。
				
				char s[]={'a','b','c','d','e'};
				
					sizeof(s) == 5
				
				char s[]={"abcde"};
					=>char s[]={'a','b','c','d','e','\0'};
					
					sizeof(s) == 6
					//s是一个字符数组,数组区域是可读可写的
				
				例子:
					char s[]={"abcde"};
					=>char s[]={'a','b','c','d','e','\0'};
					s[1]='B';
					*(s+1)='B';
					
					printf("%s\n",s);
					//%s -> char *地址
					//把后面的那个地址当作是一个字符串的首地址,一个一个字符
					//打印,直到遇到\0
					
			练习:
				1.分析如下程序的输出结果?
				char*p="123456";
				*(p+1)='B';//有毛病 
				p[1]='B';//有问题的 
				p="abcde";
				printf("%s\n",p);//"abcde"
				
				---------------
				char s[]="123456";
				s[1] = 'B';//没问题
				*(s+1) = 'B';//没问题
				s="abcde";//s是数组名 是一个常量指针,不具体左值
							//error
				printf("%s\n",s);
				
				2.写一个函数求一个字符串的长度(包含了多少个字符)
				int my_strlen(char *p)
				{
					int n=0;//
					//遍历这个字符串 直到遇到'\0'
					while(*(p++)!='\0')
					{
						n++;
					}
					return n;
				}
				
		11.几个常见的字符串处理函数(标准库里面的)
			1)strlen:用来求一个字符串的长度
				man:查看帮助文档的 
					选项: -f 查看文档的目录
				eg: man 3 printf
				man 
				NAME
   strlen - calculate the length of a string

	SYNOPSIS
		   #include <string.h>

		   size_t strlen(const char *s);
			S:要计算长度的字符串的首地址
			返回值:
				返回字符串s的一个\0前面的字符个数

	DESCRIPTION
		   The  strlen() function calculates the length of the string pointed to
		   by s, excluding the terminating null byte ('\0').

	RETURN VALUE
		   The strlen() function returns the number of characters in the  string
		   pointed to by s.
	例子:
		strlen("abcde) == 5
		char s[4]={'1','0'};
		int l;
		l=strlen(s);
		l=2
		------------
		l=strlen("abcd\nabc");
		l=8
		------------
		l=strlen("123\123abc\0abc");
		l=7???
		------------
		l=strlen("123\0123abc\0abc");
		l=8
	
	2)strcpy/strncpy
		字符串拷贝函数
		把一个字符串拷贝到另一个字符串中去
		
		NAME
    strcpy, strncpy - copy a string

	SYNOPSIS
		   #include <string.h>

		   char *strcpy(char *dest, const char *src);
			用来把src指向的字符串,拷贝到dest指向的空间中去。
			直到遇到\0
			dest:destination 目的地
			src:source 源 从哪里
			返回值:
				返回拷贝后目的地的字符串的首地址
		例子:
			char s[6];
			strcpy(s,"12345");
			
			printf("%s\n",s);//12345
			
			但是,srcpy有一个巨大的BUG!!!
			你知道的,它没有考虑越界的问题,有可能导致内存的越界或非法访问。
			
			char s1[8];
			char s2[8]={"ABCD"};
				//一般的编译器会把s1和s2放到连续的地址空间上去
				
			printf("s1=%p\n s2=%p\n",s1,s2);
			
			strcpy(s1,"123456789");
			
			printf("%s\n",s1);
			printf("%s\n",s2);
			s1=0x7fffa7eec038
			s2=0x7fffa7eec040
					s1              s2
			|______________||_______________|
			 12345678        9\0CD
			123456789
			9
			
			strncpy正是为了解决strcpy的这个BUG的(strcpy没有考虑dest指向的空间的
			大小问题)
		   char *strncpy(char *dest, const char *src, size_t n);
		   把src指向的字符串前面顶多n个字符拷贝到dest指向的空间中去
			1)最多复制n个src字节。
			2)如果在src的前n个字节中没有\0,dest中的字符串不会以\0结尾。
			3)如果src的长度小于n,则strncpy()将额外的空字节写入dest以确保
			写入的字节数为n
			
			strncpy(char *dest, const char *src, size_t n)
		   {
			   size_t i;

			   for (i = 0; i < n && src[i] != '\0'; i++)
				   dest[i] = src[i];
			   for ( ; i < n; i++)
				   dest[i] = '\0';

			   return dest;
		   }
		   
		练习:
			分析如下程序的输出结果?
				char s1[8];
				char s2[8]={"ABCDE"};
				
				printf("s1=%p\n s2=%p\n",s1,s2);
				//一般的编译器会把s1和s2放到连续的地址空间上去
				strncpy(s1,"123456789",8);
				
				printf("%s\n",s1);//12345678   //12345678 //12345678ABCDE
				printf("%s\n",s2);//ABCDE 

		3)strcmp/strncmp 字符串比较
			string compare
		字符串是如何比较的呢?
			一个一个字符比较 他们的ASCII码
				if c1>c2 返回1 
				if c1<c2 返回-1
				if c1==c2 则继续比较下一个字符,如果全部相等则返回0 
				
			NAME
   strcmp, strncmp - compare two strings

	SYNOPSIS
		   #include <string.h>

		   int strcmp(const char *s1, const char *s2);

		   int strncmp(const char *s1, const char *s2, size_t n);

	DESCRIPTION
		   The  strcmp()  function  compares  the two strings s1 and s2.  
		   It returns an integer less than, equal to, or
		   greater than zero if s1 is found, respectively, to be less than, 
		   to match, or be greater than s2.

		   The strncmp() function is similar, except it compares only the 
		   first (at most) n bytes of s1 and s2.

	RETURN VALUE
		   The strcmp() and strncmp() functions return an integer less than, 
		   equal to, or greater than zero if  s1  (or
		   the first n bytes thereof) is found, respectively, to be less than, 
		   to match, or be greater than s2.

		strcmp("123","ABC") <0
		strcmp("123","123\0ABC");==0
		strcmp("1234',"123")>0
		strcmp("lijun","lijunniub",4)
	4)strcat/strncat
		NAME
   strcat, strncat - concatenate two strings 
	拼接两个字符串

	SYNOPSIS
		   #include <string.h>

		   char *strcat(char *dest, const char *src);
		用来把src指向的字符串拷贝到dest指向的字符串的末尾(尾部连接)
			dest:指向目标字符串(一段空间)
				dest指向的空间,应该足够大 WHY?
			src:指向原始字符串
			返回值:
			如果成功,返回连接后的字符串的首地址
			失败,返回NULL
			例子:
				 char s1[12]={"ABCD"};
				 strcat(s1,"12345"};
				 printf("%s\n",s1);ABCD12345

			strcat 也有一个Bug! 你懂的
			so,strncat 就是用来修复strcat的这个BUG的
			
		   char *strncat(char *dest, const char *src, size_t n);
		   把src所指向的字符串拷贝到dest的末尾,但是它顶多
		   拷贝n个字符,那么strncat到底拷贝多少个字符呢?
			1)遇到\0结束
			2)即使是没有遇到\0,但是已经拷贝了n个字符,也会结束
			strncat(char *dest, const char *src, size_t n)
		   {
			   size_t dest_len = strlen(dest);
			   size_t i;

			   for (i = 0 ; i < n && src[i] != '\0' ; i++)
				   dest[dest_len + i] = src[i];
			   dest[dest_len + i] = '\0';

			   return dest;
		   }

		
		练习:
		1.分析如下程序的输出结果?
			char s[12];//
			strcat(s,"12345");
			printf("%s\n",s);//Undefine 不确定的值 why??
			
		2.写一个函数,把一个十进制的数字字符串,转成一个整数
		"123"->123
		"-456"->-456
		
		int my_atoi(char *s)//s指向的数字字符串
		{
			int num = 0;//整数的值
			int flag_minus=0;//负数 刚开始默认不是负数
			int d =0;//当前位的数值
			//1.判断符号位
			if(*s=='-')
			{
				flag_minus = 1;
				s++;//指针往后挪动 指向第二个字符
			}
			else if(*s == '+')
			{
				s++;
			}
			//2.一个一个字符的转换 
			//遍历字符串 直到遇到'\0'
			while(*s)
			{
				//‘0’-‘9’的ascii码是不是连续的
				//'9'的ASCII-'0'的ASCII=9
				d=*s-'0';
				num = num*10+d;
				s++;//指针要后挪
			}
			//3.添加符号位
			if(flag_minus)
			{
				num=num*(-1);
			}
			//4.输出
			return num;
		}
	
	atoi这个函数其实是一个标准C库里面的函数,大家可以直接用:
	man atoi 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值