一、字符指针数组 - 1
1、字符指针数组概念
字符指针数组是指一个数组中的各个元素都是字符指针
或者是字符串(字符数组)的首地址
2、初始化
char *name[]={"zhangsan","lisi","wangwu"};
变量名联系:
实际存值: name[0]=&'z' , name[1]=&'l' , name[2]=&'w'
间接功能: name[0]="zhangsan" , name[1]="lisi" , name[2]="wangsu"
3、存储方式
见->变量名联系
*每个元素存储的是字符串的[首地址]
eg:首 地 址:
&’z’ , &’l’ , &’w’
内部元素:
‘z’ , ‘h’ , ‘a’ , ‘n’ , ‘g’ , ‘s’ , ‘a’ , ‘n’
‘l’ , ‘i’ , ‘s’ , ‘i’
‘w’ , ‘a’ , ‘n’ , ‘g’ , ‘w’ , ‘u’
内部元素的地址对应的数据,是存放在常量区->不可修改
元素首地址存放到了names指针数组中,即其元素
*优先考虑字符指针数组存放字符串
减少 排序/交换等操作 时的内存消耗
4、示例程序
#include <stdio.h>
int main(void)
{
char *names[]={"zhangsan","lisi","wangsu"};
printf("names[0]: %s , names[0]: %p\n",names[0],names[0]);
*(names[0])='Z'; //段错误,存在字符串常量区,不可修改!
return 0;
}
二、字符指针数组 - 2
1、字符指针数组相关概念
names两种理解:
names数组里的第一个元素(值)
字符常量数组里第一个字符串的首地址:&’z’
程序运行中,出现了两个地址
2、概念理解
两个地址:
第一个地址,每个字符串常量的地址,和字符指针数组里面的元素的值,依次一一对应相同
第二个地址,字符指针names中的元素,需要存放的地址,这些地址值以元素的形式存放在PP指针数组里
以上构成 一级指针 和 二级指针
两种访问字符指针数组方式:
下标访问 | 二级指针(指针的指针)访问
3、深入理解二级指针概念:
p:一级指针变量(名)
a=10 p=&a
&a: 一级指针
&a->p &p->pp
pp:二级指针变量(名)
&p二级指针
解释:
二级指针变量名(二级指针里面第一个元素值)
等于一级指针变量名(一级指针里面第一个元素的值)的地址
(即: pp0= &p(地址) = names0)
等于字符指针数组(常量)(zhangsan)的地址
4、示例程序:
利用二级指针遍历字符指针数组,打印出字符串常量
#include <stdio.h>
//下标遍历
void out_name(char *names[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%s ",names[i]);
}
printf("\n");
}
//二级指针遍历
void out_name2(char **pp,int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%s ",*(pp+i));//*(&names[0]) *(pp+i)
//pp是指针,加上地址偏移量i,就是在指向p里面的元素
//*(p里面的元素) = names数组每个字符串的首地址
}
printf("\n");
}
int main(void)
{
char *names[]={"zhangsan","lisi","wangsu"};
printf("names[0]: %s , names[0]: %p\n",names[0],names[0]);
out_name(names,3);
out_name2(names,3);//names 两种理解
return 0;
}
三、字符指针数组 - 3
1、字符指针数组中存放的是字符串(字符数组)的首地址
而字符串本身存放在常量区,是不能被修改的
2、字符指针数组存储字符串要比字符数组存储字符串的开销小
操作时(如排序、交换)占用内存小
3、两种利用二级指针寻址的方式(排序)
1. void sort(char *names[],int n)
{
int i,j,pos;
for(=0;i<n-1;i++)
{
pos = i;
for(j=i+1;j<n;j++)
{
//字符串的首地址
if(strcmp(names[i],names[pos])<0)
{
//position 始终指向小值
pos = j;
}
}
}
if(pos != i)
{
char *temp;
temp = names[pos];
names[pos] = names[i];
names[i] = temp;
}
}
2. void sort2(char **p,int n)
{
int i,j,pos;
for(=0;i<n-1;i++)
{
pos = i;
for(j=i+1;j<n;j++)
{
//字符串的首地址
if(strcmp(*(pp+j),*(pp+pos))<0)
{
//position 始终指向小值
pos = j;
}
}
}
if(pos != i)
{
char *temp;
temp = *(pp+pos);
*(pp+pos) = *(pp+i);
*(pp+j) = temp;
}
}
四、字符指针数组 - 4
1、带参主函数->字符指针数组获取命令行参数的
格式:int main(int argc,char *argv[])
解释:
argc 记录传入函数的参数个数
(包括可执行文件->0开始,不包括可执行和文件->1开始)
argv 记录成功传入函数的参数内容
(包括可执行文件->0开始,不包括可执行和文件->1开始)
案例:
#include <stdio.h>
int main(int argc,char *argv[])//第二参数换成二级指针:char **argv
{
printf("argc:%d\n",argc);
int i;
for(i=1;i<argc;i++)
{
printf("%s",argv[i])); //下标法
//printf("%s",*(argv+i)); //指针法
}
printf("\n");
return 0;
}
1> 下标法->argv[i])
运行:bin/args_main 1 2 3 4 5
(1 2 3 …都是读取入该字符串首地址)
argc:6 (带上了可执行纪文件)
1 2 3 4 5
argv[i]从1开始,因为bin/a.out占用了0
eg:
gcc args_main -o args_main.c
args_main 替换 a.out
{运行:bin/args_main ni hao ma
原理:读入带参函数的是字符串首地址:
&’n’、&’h’、&’m’
输出:
argc:4 (带上了可执行文件)
ni hao ma
如果是从argv[0]开始则输出为:
argv:4
bin/args_main ni hao ma
}
2> 指针法->*(argv+i)
第二参数换成二级指针:char **argv
原理同 下标法
五、字符指针数组 - 案例
1、内容:
根据姓名查找学生是否存在
char *students[]
二级指针->遍历一维字符指针数组
“结束标志:”NULL””
2、代码
#include <stdio.h>
#include <string.h>
int find(char **pp,char *name);
//第一个参数:
//第二个参数:待查学生姓名首地址
int main(void)
{
int result;
NULL:在这里作空指针
char *students[]={"zhangsan","lisi","wangwu",NULL};
char name[20]={'\0'};
printf("Please Input Student's Name: ");
scanf("%s",name);
//students:该字符指针数组的->首地址
result = find(students,name);
if(result)
{
printf("find !");
}
else
{
printf("not find !");
}
}
int find(char **pp,char *name)
{
//Tips: 对入参数进行判断
if(pp == NULL||name == NULL)
{
return 0; //返回为->假
}
while(*pp != NULL)
{
if(strcmp(*pp,name)==0)
{
return 1; //找到了返回为->真
}
else
{
pp++; //找下一个
}
}
return 0; //遍历完了还没找到则: return false
}
要点:
NULL作为最后结尾标志
pp++超出字符指针数组存储范围后
容易产生段错 -> 越界导致