目录
前言
一、字符指针
二、指针数组
1.指针数组用法
2.声明一个指针数组:
三、多级指针
1.多级指针的意义
2.多级指针的运算
3.多级指针和数组
4.总结
四、void指针和const
1.void 指针
(1)用途案例
2.const修饰符
(1)常量化指针目标表达式 一般说明形式如下:
(2)常量化指针变量一般说明形式如下:
(3)常量化指针变量及其目标表达式 一般说明形式如下:
总结
C语言通过使用字符数组来处理字符串
通常,我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,它也被用来处理字符串。
初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中
char str[] = "hello world“;
char *p =str;
在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值
char *p = "hello world";
*p = 'h';//错误,这里字符串常量不能修改
#include
#include
int main(int argc,char *argv[]){
char ch1[] = "Hello World!";
char ch2[] = "Hello World!";
char *p;
p = ch1;
if (isalpha(*p)){
if (isupper(*p)) {
*p = tolower(*p);
}
}
printf("%s\n",p);
p = ch2;
printf("%s\n",p);
return 0;
}
//p是局部变量,可以把字符串做修改,放在栈上,自动分配自动回收
hello World!
Hello World!
这里p是局部变量,可以把字符串做修改,放在栈上,自动分配自动回收
#include
#include
int main(int argc,char *argv[]){
char *p1 = "Hello World!";
char *p2 = "Hello World!";
printf("&p1=%p %p %s\n", &p1, p1, p1);
printf("&p2=%p %p %s\n", &p2, p2, p2);
return 0;
}
//结果
&p1=0xbfc63448 0x8048520 Hello World!
&p2=0xbfc6344c 0x8048520 Hello World!
另一个用到两个指针,字符串是常量,从内存管理的角度不需要两个地址。
程序案例:不利用任何字符串函数,编程实现字符串连接函数的功能
#include
int main(int argc,char *argv[]){
char arr1[100] = "Hello ";
char arr2[] = "World!"; //char *p ="World!" 也时可以的
char *p,*q;
p = arr1;
q = arr2;
while(*p != '\0'){
p++;
}
while(*q != '\0'){
*p = *q;
p++;
q++;
}
printf("arr1=%s\n", arr1);
printf("arr2=%s\n", arr2);
return 0;
}
arr1=Hello World!
arr2=World!
所谓指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合
-指针数组的一般说明形式:
<存储类型> <数据类型> *<指针数组名>[大小];
指针数组名表示该指针数组的起始地址
所谓指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合
-指针数组的一般说明形式:
<存储类型> <数据类型> *<指针数组名>[大小];
指针数组名表示该指针数组的起始地址,主要用来把多个指针可以有序组合
#include
int main(int argc,char *argv[]){
int * p[3]; //主要用来把多个指针可以有序组合
int a[] = {3,6,1,9,10};
p[0] = a;
p[1] = a+1;
p[2] = a+3;
printf("%d %d %d\n",a[0], a[1], a[3]);
printf("%d %d %d\n",*(p[0]), *(p[1]),*(p[2]));
return 0;
}
3 6 9
3 6 9
double * pa[2],a[2][3]; //*只管pa[2]
pa[0] = a[0]; //等价于pa[0] = &a[0][0];
pa[1] = a[1] ;//等价于pa[1] = &a[1][0];
把一维数组a[0]和a[1]的首地址分别赋予指针变量数组的数组元素pa[0]和pa[1]]:
此时pa[0]指向了一维数组a[0]的第一个元素a[0];
而pa[1]指向了一维数组a[1]的第一个元素a[1][0]
如果要打印a[0][1],也可以用p[0]+1。如果找a[1][2] 可以先p[1]+2
总结:
1如何计算指针数组占用的内存空间?
答:数组名求sizeof就是指针数组的内存空间,4字节*个数
#include
int main(int argc,char *argv[]){
int * p[3];
printf("%d\n",sizeof(p));
return 0;
}
12 //4*3
2指针数组名加1,移动多少字节?
答:不管是double还是int没有关系,+1就是移动一个元素,32位系统就是指针占 4个字节
#include
int main(int argc,char *argv[]){
int * p[3];
printf("%p\n",p);
printf("%p\n",p+1);
return 0;
}
0xbfa9b2a4
0xbfa9b2a8
3指针数组名相当于什么样的指针?
答:元素地址,即一级指针取地址就是二级指针。
4指向数组的指针是什么?
答: int array[3]
int *p1 = array;
与指向数组首元素的指针是两码事,指向数组首地址的指针,需要二次解引用
#include
int main()
{
int array[3] = { 1, 2, 3 };
int i;
int *p1 = array; //指向数组的指针 int *p1; p1 = array;
int (*p2)[3];
p2 = &array; //指向数组首地址的指针,需要二次解引用
for ( i = 0; i < 3; i++)
{
printf("%d %d ", *(*p2+i),*(p1+i)); //*p2相当于array
}
}
1 1 2 2 3 3
5指向数组首地址的指针是什么?
答:int (*p2)[3]
6int*p[4]和int(*p)[4]的区别?
1、int(*p)[4];------p为指向含4个元素的一维整形数组的指针变量(是指针) 2、int *p[4];-------定义指针数组p,它由4个指向整型数据的指针元素组成(是数组) 3、int(*)[4];--------实际上可以看作是一种数据类型。也就是第一个(int(*p)[4];)中定义的p的数据类型 其实你要看这种到底是什么,就是要看他最先和谁结合。 比如1中p先与*结合,那就说明p本质是一个指针;而2中p先与后面的[4]结合,说明他本质是一个数组。再慢慢理解
#include
int main()
{
int a[4] = {1, 2, 3 ,4};
int (*p)[4] = &a; //
int i;
for ( i = 0; i < 4; i++)
{
printf("%d %d %d ", *(a+i),(*p)[i],*(*p+i));
}
}
//结果
1 1 1 2 2 2 3 3 3 4 4 4
把一个指向指针变量的指针变量,称为多级指针变量对于指向处理数据的指针变量称为一级指针变量,简称一级指针。而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针。
二级指针变量的说明形式如下 <存储类型> <数据类型> ** <指针名> ;
int * * p
指针变量加1,是向地址大的方向移动一个目标数据。类似的道理,多级指针运算也是以其目标变量为单位进行偏移。比如,int **p;p+1移动一个int *变量所占的内存空间。再比如int ***p,p+1移动一个int **所占的内存空间。
指针数组也可以用另外一个指针来处理。
例如:有一个一维字符指针数组ps[5],
char *ps[5]= { "Beijing city", …… "London city" } ;
定义另一个指针变量pps,并且把指针数组的首地址赋予指针pps
char *ps[5]={……}; char ** pps = ps;
主要介绍了多级指针,包括定义、初始化、运算以及多级指针与指针数组思考多级指针在内存中占几个字节?多级指针加1,移动多少字节?
答:如果是32位系统就是4个字节,指针+1也是移动4个字节
#include
int main(int argc,char *argv[]){
char * s[] = {"apple","pear","potato"};
char ** p;
int i, n;
i = 0;
n = sizeof(s) / sizeof(char *);
p = &s[0]; //p = s;
while (i < n){
printf("%s %s\n",s[i], *(p + i));
i++;
}
return 0;
}
//结果
apple apple
pear pear
potato potato
void指针是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量一般形式为: void * <指针变量名称> ;对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算
#include
int main()
{
int m = 10;
double n =3.14;
void * p , * q;
p = (void *)&m;
printf("%d %d\n", m, *(int *)p);
q = (void *)&n;
printf("%f %f\n", n, *(double *)q);
return 0;
}
//结果
10 10
3.140000 3.140000
分配内存;线程创建;快速拍苏等函数中均用到void *语法。提供里通用的设计,用到时在转换。
void * malloc(size_t size); 内存管理
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);线程创建
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));快速排序,
编程实现:使用void指针遍历一维数组
#include
int main()
{
int a[] = {5, 9, 6, 10, 11, 12};
int i, n;
void * p;
p = a;
n = sizeof(a) / sizeof(int);
for(i = 0; i < n; i++)
printf("%d ", *((int *)p + i));
puts("");
return 0;
}
进行算法操作的指针必须是确定知道其指向数据类型大小
#include
int main()
{
int m = 10;
double n =3.14;
void * p , * q;
p = &m;//p = (void *)&m;
printf("%d %d\n", m, *(int *)p);
printf("%p\n", p);
p++;
printf("%p\n", p);
q = &n;//q = (void *)&n;
printf("%.2lf %.2lf\n", n, *(double *)q);
printf("%p\n", p);
q++;
printf("%p\n", q);
return 0;
}
//实验结果
10 10
0xbfda752c //这里+1,下面加了12,因为没有做处理。所以验证了之前的结论,void*没有转换之前的运算不合适
0xbfda752d
3.14 3.14
0xbfda752d
0xbfda7539
常量化变量的值一般说明形式如下:
const <数据类型> 变量名 = [<表达式>] ;
常量化变量是为了使得变量的值不能修改。变量有const修饰时,若想用指针间接访问变量,指针也要有const修饰。
const放在指针声明的什么位置呢?
const <数据类型> * <指针变量名称>[= <指针运算表达式>] ;
常量化指针目标是限制通过指针改变其目标的数值 ,但<指针变量>存储的地址值可以修改
const在指针前面
const int * p
#include
int main()
{
int m = 10,n = 9;
const int * p;
p = &m;
// p = &n; //right
(*p)++; // error
return 0;
}
<数据类型> * const <指针变量名称>[= <指针运算表达式>] ;
使得<指针变量>存储的地址值不能修改。但可以通过 *<指针变量名称> 可以修改指针所指向变量的数值
const在后面
int * const q = &m;只能在这赋值
include
int main()
{
int m = 10, n = 9;
int * const p = &m;
//p = &n;//error
*p = 20;//right
return 0;
}
const <数据类型> * const <指针变量名> = <指针运算表达式> ;
常量化指针变量及其目标表达式,使得既不可以修改<指针变量>的地址,也不可以通过*<指针变量名称>修改指针所指向变量的值
#include
int main()
{
int m = 10, n = 9;
const int * const p = &m;
//p = &n;//error
//*p = 20;//right
return 0;
}
void指针有什么作用?
答:使得指针后面可以处理不同类型的数据,往往在后面的库函数提供通用返回值
main函数是否可以带参数(涉及到const指针)?
答:可以,涉及到main函数需要处理网络编程,或者处理文件就可以加参数去传递
#include
int main(int argc, const char * argv[])
{
//const char * argv[] = {"./a.out","192.168.1.5","8080"};
int i;
printf("argc=%d\n", argc);
for( i = 0; i < argc; i++){
printf("%s\n",argv[i]);
}
return 0;
}
//结果
syj@ubuntu:~$ ./a.out
argc=1
./a.out
syj@ubuntu:~$ ./a.out 192.168.1.5 8080
argc=3
./a.out
192.168.1.5
8080