目录
一、指针操作一维字符型数组
1.定义指针
char s[] = "hello"';
char *p=&a[0];
char *p = s; //数组名的值就是首元素的地址
2.指针 + 字符串
字符串 // 在c语言中是按照字符数组的形式存储
// 字符串常量 --- 存储在字符串常量区
处理字符串:
char s[] = "hello"; //表示 在栈上开辟一块空间,用字符串常量中的 "hello"进行初始化
const char *p = "hello"; //表示 p指向了 字符串常量区中的 "hello"
//因为 是指向了字符串常量区 ,所以只能做读取操作,不能修改
3.const 关键字
const //表示只读
const int a; //a 此时是一个只读的变量
const int *p = &a; //const限定是 基类型
//表示不能通过 *p 的方式 修改基类型数据
int const *p = &a; //const限定是 基类型
//表示不能通过 *p 的方式 修改基类型数据
int * const p = &a; //const限定是 指针变量p
//表示将p限定位只读
//表示p不能被修改
const int * const p = &a; //基类型和指针变量 都被限定为只读
// p = &b; //不能修改
//*p = b; //不能修改
原则:
就近原则 //const 离谁近 就限定谁
应用:
1.如果 不想 通过*p方式改变基类型对应的数据
const int *p = &a;
int const *p = &a;
2.如果 指针变量p 定义好后,不想再指向别的变量
则
int * const p = &a;
在函数中形参设计为 const char *
目的:防止函数中的误操作
好处:
(1).提前发现问题
将运行时才出现的问题,提前到编译时
(2).const char *
可以接收(可以将可读可写的传给只读的,但不能反过来,否则会使得权限扩大)
char *
const char *
实参:
可以 数组名
可以 指针变量 char *p //const char *p
可以 直接是一个字符串常量
提高参数的适用性
所以能写成const的 都写const。
gets函数,通过指针形式
#include <stdio.h>
char * mygets(char *s)
{
char *ret = s;
scanf("%c",s);
while(*s!='\n')
{
scanf("%c",++s);
}
*s='\0';
return ret;
}
int main(void)
{
char b[100];
mygets(b);
puts(b);
return 0;
}
puts函数,通过指针形式
#include <stdio.h>
int myputs(const char *s)
{
if(s==NULL)
{
return -1;
}
while(*s != '\0')
{
putchar(*s++);
}
putchar('\0');
return 0;
}
int main(void)
{
char a[]="hello";
char *p=a;
//char *p=&a[0];
const char *q="world";
myputs(p);
myputs(a);
myputs(q);
myputs("well");
return 0;
}
指向栈上的的指针,指向字符串常量区的指针,数组名,和字符串常量都可以输出。
strlen函数,通过指针形式
#include <stdio.h>
size_t mystrlen(const char *s)
{
int i=0;
while(*s!='\0')
{
++s;
++i;
}
return i;
}
int main(void)
{
char a[]="hello";
puts(a);
printf("%ld\n",mystrlen(a));
return 0;
}
strcpy函数,strncpy函数通过指针形式
char src[] = "hello"
strncpy(dest,src,8);
char *Strncpy(char *dest, const char *src, size_t n)
{
//1.始终拷贝了n下
//1. 拷贝
'\0' && n
//2. 考虑n
n有没有结束
完成剩余的次数的拷贝
剩余的拷贝过去的数据是 '\0'
}
#include <stdio.h>
char * mystrcpy(char *dest,const char *src)
{
char *ret=dest;
while(*src!='\0')
{
*dest=*src;
dest++;
src++;
}
*dest='\0';
return ret;
}
char * mystrncpy(char *dest,const char *src,size_t n)
{
char *ret=dest;
int i=0;
while(*src!='\0' && i!=n )
{
*dest=*src;
dest++;
src++;
i++;
}
if(i<n)
{
int j=0;
for(j=0;j<(n-i);++j)
{
*dest='\0';
dest++;
}
}
return ret;
}
int main(void)
{
char a[100]="helloworld";
char b[100]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
puts(a);
mystrncpy(b,a,15);
puts(b);
int i=0;
for(i=0;i<20;++i)
{
printf("b[%d]%c %d\n",i,b[i],b[i]);
}
return 0;
}
strcat函数,strncat函数通过指针形式
char *strcat(char *dest, const char *src)
{
//1.定位 dest的 '\0'
//2.拷贝
直到src到达'\0'
}
char src[] = "hello";
strncat(dest,src,3);
char *Strncat(char *dest, const char *src, size_t n)
{
//1.可以指定 n
//如果 src 长度 > n
就将前n个字符拼接过去
//如果 src 长度 < n
直接将src字符串拼接过去
最终 一定要保证 dest是一个字符串 '\0'
}
#include <stdio.h>
char * mystrcat(char *dest,const char *src)
{
char *ret=dest;
while(*dest!='\0')
{
dest++;
}
while(*src!='\0')
{
*dest=*src;
dest++;
src++;
}
*dest='\0';
return ret;
}
char * mystrncat(char *dest,const char *src,size_t n)
{
char *ret=dest;
while(*dest!='\0')
{
dest++;
}
while(*src!='\0'&&n!=0)
{
*dest=*src;
dest++;
src++;
n--;
}
*dest='\0';
return ret;
}
int main(void)
{
char a[100]={'h','e','l','l','o','\0',1,1,1,1,1,1,1,1,1,1,1,1};
char b[100]="world";
mystrncat(a,b,3);
puts(a);
int i=0;
for(i=0;i<20;++i)
{
printf("a[%d]%c %d\n",i,a[i],a[i]);
}
strcmp函数,strncmp函数通过指针形式
#include <stdio.h>
int mystrcmp(const char *s1,const char *s2)
{
while(*s1==*s2 && *s1!='\0' && *s2!='\0')
{
s1++;
s2++;
}
return *s1-*s2;
}
int mystrncmp(const char *s1,const char *s2,size_t n)
{
while(*s1==*s2 && *s1!='\0' && *s2!='\0'&& n>1)
{
s1++;
s2++;
n--;
}
return *s1-*s2;
}
int main(void)
{
char a[100]="hello";
char b[100]="help";
puts(a);
puts(b);
int ret = mystrncmp(a,b,3);
printf("%d\n",ret);
return 0;
}
n减到0的时候,两个字符串最后停位置是在后一个,所以条件是n>1而不是n不为0。
以上函数的返回值有些是如char *的指针类型,这是为了进行链式操作
eg: char s1[10];
char s2[10];
strcpy(s2,strcpy(s1,"hello"));//链式操作
strcpy只能拷贝字符串,而memcpy可以拷贝任意类型的数据,只要有地址。但要注意不同类型之间拷贝的话,可能会出现数据覆盖。
void *memcpy(void *dest, const void *src, size_t n)
{
//一个字节一个字节拷贝(char类型刚好是一个字节)
}
void * //NULL 空指针
//空类型的指针 --- 万能指针
//可以接收任意类型的指针
注意:如果用空类型指针进行间接运算必须转换成有明确类型的指针
#include <stdio.h>
#include <string.h>
void * mymemcpy(void *dest, const void *src, size_t n)
{
char *p=dest;
const char *q=src;
int i=0;
for(i=0;i<n;++i)
{
*p=*q;
p++;
q++;
}
return p;
}
int main(void)
{
long a[5]={1,2,3,4,5};
long b[5];
mymemcpy(b,a,5*sizeof(a[0]));
int i=0;
for(i=0;i<5;++i)
{
printf("%ld\n",b[i]);
}
return 0;
}
二、指针操作二维数组
int a[2][3];
//二维数组
c语言中并不存在,真正的二维数组。
二维数组本质 是一维数组的一维数组
二维数组 也 符合数组的特点 //连续性,有序性,单一性
//从二维数组的本质出发
int[3] a[2];
1.定义指针
//a[0] --- int[3]
//&a[0] --- int[3] *
//c语言中 不支持 int[3] *
//正确写法 int(*)[3]
int(*p)[3] = a; //p指向二维数组 a
//p的基类型 int[3]
2.访问数组中的元素
*p <=> a[0] // 相当于是内部这个一维数组的数组名
a[0][0] <=> (*p)[0] //一维数组的元素是通过*(p+i)访问的
*(*(p+0)+0) //**p
*(*(p+1)+1) <=> a[1][1]
*(*(p+i)+j) <=> a[i][j]
二维数组的本质 一维数组的 一维数组