c语言 字符串和数组指针,C语言数组与指针一本道来
数组与指针.png
指针的基础
注意本节内容可能在gcc下不能完成编译,请切换到Windows平台,使用dev-cpp或其他
指针本质上也是一个变量
指针要占用一定的内存空间(任何类型的指针的内存大小是一样的)
指针用于保存内存地址的值
*号的意义
在指针声明时,*号表示所声明的变量为指针
*号表示取指针指向的内存空间中的值
最佳实践(指针占用的内存空间)
#include
int main()
{
int i;
int* pI;
char* pC;
float* pF;
pI = &i;
printf("%0X, %0X, %d\n", pI, &i, i);
printf("%d, %d, %0X\n", sizeof(int*), sizeof(pI), &pI);
printf("%d, %d, %0X\n", sizeof(char*), sizeof(pC), &pC);
printf("%d, %d, %0X\n", sizeof(float*), sizeof(pF), &pF);
return 0;
}
最佳示例(写地址)需要在VS或其他windows系统下测试,
#include
int main()
{
int i;
int * p;
p=&i;
*((int*)0x28FEB8)=100;
printf("%0X\n",p);
printf("%d\n",i);
return 0;
}
传值调用与传址调用
当一个函数体内部需要改变实参的值,则需要使用指针参数
函数调用时实参将赋值到形参
数组的基础
数组在一片连续的内存中存储元素
元素的个数可以是显示的也可以是隐式的
例子
a[5]={1,2}
其中其他的都用0来填充,所以数组最后为
a[5]={1,2,0,0,0}
数组地址与数组名
数组名代表数组首元素的地址
数组的地址需要取地址才可以得到
数组首元素的地址值与数组的地址值相同
数组收元素的地址与数组的地址是两个不同的概念
数组名的盲点
数组名可以看作一个常量指针(指针所指向的内容不能改变)
数组“指向” 的是内存中数组首元素的起始位置
在表达式中数组名只能作为右值使用
只有在下列场合中数组名不能看作常量指针
数组名作为sizeof操作符的参数
数组名作为& 运算符的参数
最佳错误
新建一个.c文件
char * p="hello world";
再建一个.c文件
#include
extern char p[];
void main()
{
printf("%s\n",p);
}
思路很简单,就是输出hello world,然而输出的结果是错误的,原因就是上面列出来的几点中的一点。
修改
Linux
#include
extern char p[];
void main()
{
printf("%s\n",(char *)*(unsigned int *)p);
}
Windows平台
#include
extern char p[];
void main()
{
printf("%s\n",(char *)*(unsigned int *)p);
}
虽然会有warning但是程序运行是正确的
C语言中的字符串
从概念上讲,C语言中没有字符串数据类型
在C语言中使用字符数组来模拟字符串
C语言中的字符串是以'\0'结束的字符数组
C语言中的字符串可以分配于栈空间,堆空间或者只读存储区(不能被改变)
char* s1="Hello World1";(在只读存储区)
字符串的长度
字符串的长度就是字符串说包含字符的个数
C语言中的字符串的长度值得是第一个'\0'字符串出现的字符个数
C语言中通过'\0'结束来确定字符串的长度
标准库,用来解决字符长度
strlen()
彩蛋(一行程序实现strlen)
int strlen(const char * p)
{
return (assert(s),(* p? strlen(p+1)+1:0));
}
不受限制的字符串函数
不受限制的字符串函数是通过寻找字符串得结束符'\0'来判断长度
字符串复制函数: char* strcpy(char* dst,const char* src)
字符串连接:char* strcat(char* dst,const char* src)
字符串比较函数:int strcmp(const char* s1,const char* s2)
注意事项
不受限制的字符串函数都是以'\0'作为结束标记来进行的,因此输入参数必须包含'\0'
strcat和strcpy必须保证目标字符数组的剩余空间足以保存整个源字符串
strcmp以0值表示两个字符串相同
第一个字符串大于第二个字符串的时候返回值大于0
第一个字符串小于第二个字符串的时候返回值小于0
strcmp不会修改参数值,但依然以'\0'作为结束符号
彩蛋(实现strcpy)
char* strcpy(char* dst,const char* src)
{
char* ret=dst;
assert(dst && src);
while((*dst++=*src++)!='\0');//指针循环效率高
return ret;
}
长度受限制的字符串函数
长度受限的字符串函数接收一个显示的长度参数用于限定操作的字符串
字符串复制:char* strncpy
字符串连接
字符串比较
指针数组和数组指针分析
数组类型
C语言中的数组有自己特定的类型
数组的类型由元素类型和数组大小共同决定
int array[5]的类型为int[5]
定义数组类型
C语言中通过typedef为数组类型重命名
typedef type(name)[size];
数组类型(重命名了一种一个数组类型):
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
数组定义:
AINT5 i
AFLOAT10 f
数组指针
数组指针用于指向一个数组
数组名是数组首元素的起始地址,但并不是数组的起始地址
通过将取地址符号&作用于数组名可以得到数组的起始地址
可通过数组类型1定义数组指针:ArrayType* pointer;
也可以直接定义:type (*pointer)[n];(用于定义数组指针)
pointer为数组指针的变量名
type为指向的数组的类型
n为指向的数组的大小
最佳示例
#include
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
typedef char(ACHAR9)[9];
int main()
{
AINT5 a1;
float fArray[10];
AFLOAT10* pf = &fArray;
ACHAR9 cArray;
char(*pc)[9] = &cArray;
char(*pcw)[4] = cArray;
int i = 0;
printf("%d, %d\n", sizeof(AINT5), sizeof(a1));
for(i=0; i<10; i++)
{
(*pf)[i] = i;
}
for(i=0; i<10; i++)
{
printf("%f\n", fArray[i]);
}
printf("%0X, %0X, %0X\n", &cArray, pc+1, pcw+1);
//pc+1指向整个数组的后一个数组
}
指针数组
指针数组是一个很普通的数组
指针数组中每个元素为一个指针
数组指针的定义:type* pArray[n];
type*为数组中每个元素的类型
pArray为数组名
n为数组大小
#include
#include
int lookup_keyword(const char* key, const char* table[], const int size)//第二个是指针数组
{
int ret = -1;
int i = 0;
for(i=0; i
{
if( strcmp(key, table[i]) == 0 )
{
ret = i;
break;
}
}
return ret;
}
#define DIM(a) (sizeof(a)/sizeof(*a))
int main()
{//指针数组
const char* keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
"case",
"static"
};
printf("%d\n", lookup_keyword("return", keyword, DIM(keyword)));
printf("%d\n", lookup_keyword("main", keyword, DIM(keyword)));
}
main函数的参数
main函数可以理解为操作系统调用的函数
在执行程序的时候可以向main函数传递参数
main函数的四种参数类型
int main()
int main(int argc)
int main(int argc,char *argv[])
int main(int argc,char *argv[],char *env[])
小结
数组指针本质上是一个指针
数组指针指向的值是数组的1地址
指针数组本质上是一个数组
指针数组中的每个元素的类型是指针
多维数组和多维指针
指针变量在内存中会占用一定的空间
可以定义指针来保存指针变量得地址值
int main()
{
int a=0;
int* p=NULL;
int** pp=NULL;
pp=&p;
*pp=&a;
return 0;
}
指向指针的指针
为什么需要指向指针的指针?
指针在本质上也是一个变量
对于指针来讲也有传值调用与传址调用
最佳示例(动态内存大小更变)
#include
#include
int rest(char** p,int size,int new_size)
{
int ret=1;
int len=0;
int i=0;
char* mid=NULL;
char* pt=NULL;
char* pp=*p;
if((p!=NULL)&&(new_size>0))
{
mid=(char*)malloc(3);
pt=mid;
len=(size
for(i=0;i
{
*pt++=*pp++;
}
free(*p);
*p=pt;
}else{
ret=0;
}
return ret;
}
void main()
{
char *p=(char*)malloc(5);
printf("%0X\n",p);
if(rest(&p,5,3))
{
printf("%0X\n",p);
}
}
二维数组与数组指针
二维数组在内存中以一维的方式排布
二维数组中的第一维是一维数组
二维数组中的第二维才是具体的值
二维数组的数组名可以看作常量指针
二维数组.png
#include
#include
void printArray(int a[],int size)
{
int i=0;
printf("printfArray:%d\n",sizeof(a));
for(i=0;i
{
printf("%d\n",a[i]);
}
}
int main()
{
int a[3][3]={{0,1,2},{3,4,5},{6,7,8}};
int *p=&a[0][0];
printArray(p,9);
return 0;
}
数组名
一维数组名代表数组收元素的地址
二维数组同样代表数组首元素的地址
#include
int main()
{
int a[5][5];
int (*p) [4];
p=a;
printf("%d\n",&p[4][2]-&a[4][2]);
}
答案为-4,因为(*p)一次跨越4个,而a一次跨越5个
数组的遍历
int a[3][3]={{}};
for(i=0;i<3;i++)
for(j=0;j<3;j++)
*(*(a+i)+j)
动态分配二维数组
原理大家很聪明就不解释
二维数组动态分配.png
#include
#include
int** malloc2d(int row,int col)
{
int** ret=(int**)malloc(sizeof(int*)*row);
int* p=(int*)malloc(sizeof(int)*row*col);
int i=0;
if(ret&&p)
{
for(i=0;i
{
ret[i]=p+col*i;
}
}else{
free(ret);
free(p);
ret=NULL;
p=NULL;
}
return ret;
}
void del_Array(int** a)
{
free(a);
}
void main()
{
int i=0,j=0;
int row=0,col=0;
printf("please input the row\n");
scanf("%d",&row);
printf("please input the col\n");
scanf("%d",&col);
int** p=malloc2d(row,col);
for(i=0;i
{
for(j=0;j
{
p[i][j]=i+j;
}
}
for(i=0;i
{
for(j=0;j
{
printf("%d ",p[i][j]);
}
printf("\n");
}
}
数组参数和指针参数分析
注意C语言的编译器会让(不论是一维数组还是二维数组)数组参数退化为指针
为什么退化
C语言中只会以值拷贝的方式传递参数
当向函数传递数组时
将整个函数拷贝一份传入函数(不可取)
将数组名看作常量指针传数组首元素地址
二维数组参数
二维数组参数同样存在退化问题
二维数组可以看作是一维数组
二维数组中的每一个元素是一维数组
二维数组参数中第一维的参数可以省略(退化过程)
void f(int a[10])->void f(int a[])->void f(int *a)
void g(int a[3][3])->void g(int a[][3])->void g(int (*a)[3])
注意事项
C语言中无法向一个函数传递任意的多维数组(针对二维以上)
为了提供正确的指针运算,必须提供除一维之外的所有维的长度
限制
一维数组-必须提供结束的标志
二维数组-不能直接传递给函数
多维-无法使用
#include
#include
void access(int a[][3],int row)
{
int col=sizeof(*a)/sizeof(int);//去推导出列的数量
int i=0,j=0;
for(i=0;i
{
for(j=0;j
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
void main()
{
int a[3][3]={
{1,2,3},
{4,5,6},
{7,8,9}
};
access(a,3);
}
函数与指针分析
函数
函数类型
C语言中的函数有自己特定的类型
函数的类型由返回值,参数类型和参数个数共同决定的
C语言中通过typedef为函数类型重命名
typedef type name(parameter list)
typedef int f(int,int);
函数指针
函数指针用于指向一个函数
函数名是执行函数体的入口地址(类似于数组名字)
可通过函数类型定义函数指针:FuncType* pointer;
也可以直接定义:type (*pointer)(parameter list);
pointer为函数指针变量名
type为指向函数的返回值
参数类型的列表
函数指针的本质与使用
回调函数
回调函数是利用函数指针实现的一种调用机制
回调机制的原理
调用者不知道具体事件发生的时候需要调用的具体函数
被调用函数不知道何时被调用,只知道被调用后需要完成的任务
当具体事件发生时,调用者通过函数指针调用具体函数
回调机制的将调用者和被调用的函数分开,两者互相不依赖
最佳示例C语言回调函数
#include
typedef int(FUNC)(int);
int test(int i)
{
return i * i;
}
void f()
{
printf("Call f()...\n");
}
int main()
{
FUNC* pt = test;
//void(*pf)() = &f;//老方法
//pf();
//(*pf)();//老方法
printf("Function pointer call: %d\n", pt(2));
}
进阶回调
#include
typedef int(*FUNCTION)(int);
int g(int n, FUNCTION f)
{
int i = 0;
int ret = 0;
for(i=1; i<=n; i++)
{
ret += i*f(i);
}
return ret;
}
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return 2*x - 1;
}
int f3(int x)
{
return -x;
}
int main()
{
printf("x * f1(x): %d\n", g(3, f1));//注册
printf("x * f2(x): %d\n", g(3, f2));
printf("x * f3(x): %d\n", g(3, f3));
}
c语言 字符串和数组指针,C语言数组与指针一本道来相关推荐
- c语言字符串 从后拷贝,C语言字符串拷贝
C语言字符串拷贝利用指针操作,要清楚知道指针的指向 代码如下: #include #include #include char* my_strcpy1(char* dest, const char* ...
- c语言字符串算法判断实验原理,C语言第二次实验报告
一.实验题目,设计思路,实现方法 第十一次作业(二维数组): 11-5 打印杨辉三角(20 分) 本题要求按照规定格式打印前N行杨辉三角. 输入格式: 输入在一行中给出N(1≤N≤10). 输出格式: ...
- 大学C语言字符串题目,[转载]大学C语言考试题精选
一. 填空题(每题2分,共20分) 1. C语言基本数据类型有:_______________________________: 构造类型有:_____________________________ ...
- C语言字符串类型定义(二维字符数组模拟连续存储多个字符串)(以小凡点名为例)
经过dev-Cpp检验 #define _CRT_SECURE_NO_WARNINGS /* 老师让小凡来完成点名,让小凡在早自习的时候就点好名.老师给了小凡名单,小凡只要照着名单点名就好了是不是很简 ...
- c语言字符串反转栈,【C语言】利用栈将数组中字符串逆序
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include"stdio.h" #include"stdlib.h" #define STACK_INIT_S ...
- c语言字符串转成二进制,C语言中字符串如何转换为二进制、八进制、十进制、十六进制...
在C语言某个程序当中需要把文本16进制转换成对应的16进制数,比如字符串"0x1a"转换成10进制的26,可以用以下函数来实现 相关函数: atof, atoi, atol, st ...
- c语言 字符串 strncpy,详解c语言中的 strcpy和strncpy字符串函数使用
详解c语言中的 strcpy和strncpy字符串函数使用 strcpy 和strcnpy函数--字符串复制函数. 1.strcpy函数 函数原型:char *strcpy(char *dst,cha ...
- c语言 字符串拷贝函数作用,C语言不使用strcpy函数如何实现字符串复制功能
Ⅰ )字符串复制函数 字符串复制是字符串操作中比较常用的操作之一.C语言库函数中提供的字符串复制函数是:strcpy函数.该函数的功能为:把源字符数组中的字符串复制到目的字符数组中,字符串结束标志&q ...
- c语言字符串操作面试题,C语言常见字符串面试题.pdf
C语言常见字符串面试题 1. 字符串库函数的内部实现 memset库函数的实现 /* * memset - Fill a region of memory with the given value * ...
- c语言字符串字节数函数,C语言字符,字符串,字节操作常用函数
strlen 这个函数是在 string.h 的头文件中定义的 它的函数原型是 size_t strlen( const char ); size_t 是一个无符号整型,是这样定义的 typedef ...
最新文章
- Laravel Predis Error while reading line from the server.
- IBM Watson:用人工智能提升美国零售业消费体验
- 处理一键安装LNMP环境之后,phpinfo.php打开不显示内容的问题
- android 开发 分辨率,Android手机应用开发为适应不同分辨率你应该知道的
- 一个走过太多坑的老弟对面向对象知识的总结:世上无难事,有的真不行
- 800万,这位两院院士全捐了!
- Java版单链表讲解
- 7个使用JavaScript构建数据可视化的实用工具库,希望你能喜欢
- Uber首位App开发者离职自述:我们如何从3人小作坊变成大公司
- 做微信营销你知道男女用微信的习惯吗?
- .NET 下运用策略模式
- 常见mysql优化 面试题
- 公众号关键字自动回复内容点击跳转小程序方法
- 廊坊金彩教育:怎么做主图
- 苹果低头了,但不想丢掉它的皇冠 1
- stanford coreNLP简单使用
- 阶梯博弈(Staircase Nim)
- 配色工具KULER的使用
- 如何让spyder中途暂停(之前运行出来的结果仍然显示)
- 微信快捷回复怎么设置?
热门文章
- 编译linux内核常见报错(最全)总结讲解
- kali系统安装详细(跟着图片一步一步进发)
- python图像质量评价_图像质量评价(三):FSIM
- 如何正确的对拍程序?
- linux mysql开启事务_linux mysql 相关操作命令
- 缺少文件 libarclite_iphoneos.a (Xcode 14.3)
- 汉字读半边,怎么会错上天?
- HDFS shell命令操作大全
- HTML5期末大作业:食品商城网站设计——食品商城购物网站(8页) HTML+CSS+JavaScript 静态网页的制作
- 微信小程序上传图片组件,多选+单选+预览+删除