字符数组和字符指针
字符串的实现
在C语言中,表示一个字符串有以下两种形式:
- 用字符数组存放一个字符串,
- 用字符指针指向一个字符串
案例:
/**
*字符串的两种实现方式
*/
//方式1:使用字符数组实现字符串
char str[]="I LOVR YOU";
printf("%s\n",str);
//使用字符指针实现字符串
char *str="I LOVE YOU";
printf("%s\n",str);
注意:字符数组和字符指针变量都能实现字符串的存储与运算
字符数组和字符指针的联系
- 字符数组由元素组成,每个元素存放一个字符,而字符指针变量存放的是地址,也能作为函数参数
- 只能对字符数组中的各个元素赋值,而不能用赋值语句对整个字符数组赋值。
char arr[3];
arr[2] = 'A';
arr = {'A','B','C'}; // 错误,(可以理解为数组名就是一个常量,也就是一旦创建,就不能再改变)
- 字符数组名虽然代表地址,但数组名不能改变,因为数组名是常量。
- 对于字符串中字符的存取,可以用下标法,可以用指针。
//使用两种方式定义字符串
char str1[]="奥丁当当";
char *str2[]="奥丁当当";//我们将数据类型为char的指针变量称之为字符指针
//测试赋值
//str1 = "程序";//不能对字符数组整体赋值,如果要赋值,请使用string库下的strcpy();
str2="奥丁当当"
//打印输出
printf("%s,%s\n",str1,str2);
char a[]="奥丁当当";
char *b[]="奥丁当当";
// 使用下标法和指针法来访问字符串
printf("%c,%c,%c,%c\n",a[2],*(a+2),b[2],*(b+2));
字符串作为形式参数
- 实参与形参都可以是字符数组
void fun(char str[],int len){...};
void main
{
char str[] = "hello";
fun(str,sizeof(str)/sizeof(str[0]);
}
- 实参用字符数组,形参用字符指针(在函数内部不能对字符串中的字符做修改,gcc编译环境可通过)
void fun(char *str,int len){
str[2] = 'A'; // GCC编译环境可通过
}
void main()
{
char str[] = "hello";// 常量池,此时的赋值,将常量池中的数据读取出来,存入到栈中数组对应的位
置
fun(str,sizeof(str) / sizeof(str[0]);
}
- 实参和形参都是指针变量(在函数内部不能对字符串中的字符做修改)
void fun(char *str,int len){
str[2] = 'A'; // 错误,字符串常量一旦创建,就不能被改变
}
void main()
{
char *str = "hello";
fun(str,sizeof(str) / sizeof(str[0]);
}
- 实参是指针类型,形参是字符数组(在函数内部不能对字符串中的字符做修改)
void fun(char str[],int len){
str[2] = 'A'; // 错误,字符串常量一旦创建,就不能被改变
}
void main()
{
char *str = "hello";
fun(str,sizeof(str) / sizeof(str[0]);
}
注意:
- 字符数组在创建的时候,会在内存中开辟内存空间,内存空间可以存放字符数据;字符指针在创建的时候,需要依赖于字符数组,字符指针在内存开辟的内存空间中,存放的是数组元素的地址。字符指针的创建依赖于字符数组,字符数组可以独立存在,而字符指针不能独立存在。
- 字符数组可以初始化,但是不能赋值;字符指针可以初始化,也可以赋值。
案例
#include <stdio.h>
/**
* 定义一个函数,实现字符串的拷贝,返回字符串长度
* @param source 拷贝的源字符串
* @param target 需要保存拷贝数据的目标字符串
* @return 字符串的大小
*/
int str_copy(char *source,char *target)
{
// 定义一个循环变量
int i = 0;
while(source[i]!='\0')
{
// 实现拷贝
*(target+i) = *(source+i);// 指针法
// target[i] = source[i];// 下标法
i++;
}
// 拷贝结束后,一定要给target末尾加上\0
target[i] = '\0';
return i;
}
int main(int argc,char *argv[])
{
// 定义两个数组,从键盘录入字符串
char source[20],target[20];
printf("请输入一个字符串:\n");
scanf("%s",source);
int len = str_copy(source,target);
printf("%s,%s,%d\n",source,target,len);
return 0;
}
案例:
#include <stdio.h>
/**
* 定义一个函数,实现字符串的截取
* @param source 源字符串
* @param start 开始截取的位置
* @param end 截取结束的位置
* @param target 截取后的字符串
* @return 新字符串长度
*/
int str_split(char *source,int start,int end,char *target)
{
// 定义循环变量
int i = 0, k = 0;
// 遍历源字符串(数组)
while(source[i] != '\0')
{
// 根据位置截取
if(i >= start && i < end)
{
// 将截取的字符串存入target "helloworld"
target[k] = source[i];
k++;
}
i++;
}
// 新字符串需要末尾添加\0
target[k] = '\0';
return k;
}
int main(int argc,char *argv[])
{
char *str = "abcdefg";
char target[100];
int len = str_split(str,2,5,target);
printf("%s,%s,%d\n",str,target,len);
return 0;
}
函数指针与指针函数
函数指针
定义:函数指针本质上是指针,它是函数的指针(定义了一个指针变量,变量中存储了函数的地址)。函数都有一个入口地址,所谓指向函数的指针,就是指向函数的入口函数。这里函数名就代表入口地址。
函数指针存在的意义:
- 让函数多了一种调用方式
- 函数指针作为形参,可以形式调用(回调函数)
定义格式:
返回值类型 (*变量名)(形式参数列表);
举例:
int (*p)(int a,int b);
函数指针的初始化:
- 定义的同时赋值
// 函数指针需要依赖于函数,先有函数,再有指针
// 定义一个普通的函数
int add(int a,int b){ return a + b;}
// 定义一个函数指针,并给他赋值
// 通过以下代码我们发现:函数指针的返回类型和依赖函数的返回类型一致,函数指针的参数个数类型和依赖函
数一致
int (*p)(int a,int b) = add;// 赋值一定要注意:函数不能带有()
- 先定义后赋值
// 定义一个普通的函数
int add(int a,int b){ return a + b;}
// 定义一个函数指针
// int (*p)(int a,int b);
int (*p)(int,int); // 一般写作这种
// 给函数指针赋值
p = add;
注意:
- 函数指针指向的函数要和函数指针定义的返回值类型,形参列表对应,否则编译报错
- 函数指针时指针,但不能指针运算,如p++等,没有实际意义
- 函数指针作为形参,可以形成回调
- 函数指针作为形参,函数调用时的实参只能是与之对应的函数名,不能带()
- 函数指针的形参列表中的变量名可以省略
案例:
#include <stdio.h>
/**
* 定义一个函数指针
*/
int max(int a,int b)
{
if(a > b)
return a;
return b;
}
int main(int argc,char *argv[])
{
// 定义测试数据
int a = 3,b = 2,c;
// 直接函数调用
c = max(a,b);
printf("%d,%d两个数中的最大值是%d\n",a,b,c);
// 定义一个函数指针
int (*p)(int,int) = max;
// 间接函数调用
c = p(a,b);
printf("%d,%d两个数中的最大值是%d\n",a,b,c);
c = (*p)(a,b);
printf("%d,%d两个数中的最大值是%d\n",a,b,c);
return 0;
}
指针函数
定义:本质上是函数,这个函数的返回值类型是指针,这个函数称为指针函数。
语法:
指针类型 函数名(形参列表)
{
函数体;
return 指针变量;
}
// int *get(int a)
int* get(int a)
{
int *b = &a;
return b;
}
int main()
{
int *a = get(5);
printf("%d\n",*a);
}
注意:
在函数中不要直接返回一个局部变量的地址。因为函数调用完毕后,局部变量会被回收,使得返回的地址就不明确,此时返回的指针就是野指针
解决方案:
如果非要访问,可以给这个局部变量添加一个关键字static
,可延长它的生命周期,避免野指针(尽量少用,因为存在内存泄漏)
案例:
#include <stdio.h>
/**
* 定义一个函数,传入学生的序号,返回这个学生的所有课程成绩
* @param p 二维数组
* @param n 学生索引(二维数组中行号)
* @return 学生成绩(行号对应的列数组)
*/
float* search(float (*p)[4],int n)
{
// 定义一个指针,用来接收查询到的某个学生的所有课程
float *pt;
pt = *(p+n);// *(p+n),*p[n],p[n]
return pt;
}
int main(int argc,char *argv[])
{
// 准备一个二维数组
float score[][4] = {{60,70,80,89},{55,66,77,88},{90,89,90,91}};
int m;
float *p;
printf("请输入学生序号(0~2):\n");
scanf("%d",&m);
printf("第%d个学生的成绩:\n",m);
// 用来接收某个学生的所有成绩
p = search(score,m);
// 遍历成绩
for(int i = 0;i < 4;i++)
printf("%5.2f\t",*(p+i));
printf("\n");
return 0;
}
标签:字符,int,C语言,char,str,字符串,指针
From: https://blog.csdn.net/2403_88805387/article/details/144390466