文章目录
数组
一组数据类型相同的元素的集合。各个元素数据类型相同,地址连续。
定义数组
存储类型 数据类型 数组名[元素个数];
在内存中分配了一片(元素个数*单位元素所占空间大小)大的空间.
数组名包含着两种含义:
1.整个数组
2.数组首元素地址。
注意:int[5]和int[6]是不同的数据类型。
数组的初始化
int arr[5]={1,2,3};//数组部分初始化。
int arr[100]={0}; //数组清零。
int arr[5]={1,2,3,4,5};//数组全部初始化。
int arr[]={1,2,3}//编译器会自动计算数组元素个数,也只有在全部初始化时才可以省略元素的个数。
数组部分初始化时,没有初始化的部分,其值为0,利用此特性可以给数组清零(即为上面的第二个式子)。
在函数体内,定义一个数组,若不初始化,则内部为随机值;
在函数体内,用static修饰一个未初始化的数组,则内部值为0;
在函数体外,定义一个数组,若不初始化,则内部为0。
数组的访问
arr[i]//数组名[下标]
scanf("%d",&arr[i]);//将输入的数传给数组的第i号元素地址内
printf("%d,",arr[i]);//打印数组第i号元素
数组的下标从0开始,故第i号元素实际是数组的第i+1个元素。
因为数组的标号特性,所以对数组元素的赋值和打印都可以通过循环来实现。
字符数组(字符串)
字符数组即内部元素为字符的数组,通常称之为字符串,控制符为“%s”。
字符数组由实质性字符和终止符“\0”组成。一个字符串所占空间通常为(实质性字符+1)*1byte。例如
char str[]="hello";//但实际上该字符串里元素为'h' 'e' 'l' 'l' 'o' '\0'
//总共6个字符
字符串的输入输出
scanf("%s",str);
gets(str);
printf("%s",str);
puts(str);
gets(数组名)函数
可以将键盘接收到的字符串存入数组中’。
gets与scanf的不同:
gets | scanf |
---|---|
只能使用回车 | 可以使用空格,回车,tab作为输入结束的标志 |
输入完毕后会清空缓冲区 | 输入完毕后会在缓冲区残留空格,回车,tab键 |
会先检查缓冲内有没有东西,如果有,则直接拿来用,没有则为键入的内容 | |
并在末尾自动添加’\0 | 不会添加终止符 |
puts(数组名)函数
可以将内存中的字符串内容打印到终端
与printf的不同:
puts | printf |
---|---|
会自动换行 | 不会换行 |
字符串专用处理函数
#include<string.h>
引入string头文件库,可以使用strlen,strcpy,strcat,strcmp函数。
函数名 | 功能 |
---|---|
strlen | 求字符串不包含’\0’的实质内容长度 |
strcpy | 将字符串b的内容连带’\0’从头赋给字符串a |
strcat | 将字符串b的内容连带’\0’从字符串a的终止符开始赋给字符串a |
strcmp | 比较数组或者字符串大小大小 |
strlen
strlen可以计算字符串不包含’\0’的字符数量,与sizeof不同的是sizeof一方面是运算符,而strlen是函数,另一方面sizeof计算的是变量所开辟的全部空间,而并不能计算实质内容所占大小。
strlen使用案例:
#include<stdio.h>
#include<string.h>
int main(){
char str[10]="1234";
int i;
i=strlen(str);
printf("%d",i);
return 0;
}
同理实现:
#include<stdio.h>
int main(){
char str[10]="1234";
int i=0;
while(str[i]!='\0'){
i++;
}
printf("%d",i);
return 0;
}
如果是sizeof(str)输出值则为10,而并非是4。
strcpy
strcpy可以将前一个字符串的内容替换为后一个字符串的内容。
strcpy使用案例:
#include<stdio.h>
#include<string.h>
int main(){
char str1[10]="123456";
char str2[10]="123";
int i=0;
strcpy(str1,str2);
puts(str1);
}
同理实现:
#include<stdio.h>
#include<string.h>
int main(){
char str1[10]="123456";
char str2[10]="123";
int i=0;
while(str2[i]!='\0'){
str1[i]=str2[i];
i++;
}
str1[i]=str2[i];
puts(str1);
}
strcat
strcat可以将后一个字符串的内容连接到前一个字符串的后部。
使用案例:
#include<stdio.h>
#include<string.h>
int main(){
char str1[25]="123456789";
char str2[25]="987654";
strcat(str1,str2);
puts(str1);
return 0;
}
同理实现:
#include<stdio.h>
int main(){
char str1[25]="123456789";
char str2[25]="987654";
int i=0,j=0;
while(str1[i]!='\0'){
i++;
}
while(str2[j]!='\0'){
str1[i]=str2[j];
i++;
j++;
}
str1[i]='\0';
puts(str1);
return 0;
}
strcmp
比较两个字符串的大小(从左往右逐个元素比较,字符比较的是ascii码大小)。
遇到不同的ascii码时停止比较并输出结果,或者是遇到’\0’结束
比较结果 | 返回值 |
---|---|
字符串1大于字符串2 | 正数 |
字符串1小于字符串2 | 负数 |
字符串1等于字符串2 | 0 |
使用案例:
#include<stdio.h>
#include<string.h>
int main(){
char str1[25]="123456789";
char str2[25]="98765431";
int result;
result=strcmp(str1,str2);
printf("%d",result);
return 0;
}
因为str在第一个元素就大于str1,所以输出值为负数。
二维数组
一组数据类型相同的一维数组的数组。
二位数组的元素是一维数组,一维数组的数据类型相同,一维数组的元素量两两相同。二位数组的所有子元素地址连续。
定义二维数组
int 数组名[一维数组数量][每个一维数组包含的元素数量];
//即int[行数][列数];
所占空间为行数*列数*单位元素空间量。
定义时可以省去行数参数,编译器可以自动计算,但是不能省略列数。
二维数组初始化
//部分初始化
int a[2][3]={1,2};
int a[2][3]={{1},{2}};
//完全初始化
int a[2][3]={1,2,3,4,5,6};
int a[2][3]={{1,2,3},{4,5,6}};
打印杨辉三角
#include<stdio.h>
int main(){
int n,x;
scanf("%d",&n);
int trg[n][n];
int i,j;
for(i=0;i<n;i++){
trg[i][0]=1;
}
for(i=1;i<n;i++){
for(j=1;j<i;j++){
trg[i][j]=trg[i-1][j-1]+trg[i-1][j];
}
trg[i][j]=1;
}
for(i=0;i<n;i++){
for(j=0;j<=i;j++){
printf("%d\t",trg[i][j]);
}
printf("\n");
}
return 0;
}
输入9后的输出结果:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
指针
程序中的所有量都存储内存当中,如果说量的名字被称为该空间的名字是量所占空间的名字,那么量的地址被称为量在内存中的门牌号,而指针即是存储地址的一种数据类型。
指针的定义
与各种量类型对应,指针存储什么类型的量就以什么类型的量来定义。
int *ip//定义一个指向整型量的指针,数据类型为: int *
float *fp//定义一个指向浮点型量的指针,数据类型:float *
double *dp//定义一个指向双精度量的指针,数据类型:double *
虽然指针型变量的数据类型随着所指向的量而改变,但是指针变量自身所占内存空间却是固定的,在32位操作系统下指针型变量占用2^32bit=4Byte,在64位操作系统下占用2^64bit=8Byte。
指针的使用
将指针指向某一量的地址,即将该量的地址赋给指针。
'&‘与’*‘是一对互逆的符号。’&‘表示取地址,’*‘则表示取该地址内存储的量。在笔者看来’*‘提供“刺穿”的功能,使用’*'从而“刺穿”包裹量的壳,之后也将使用“刺穿”这一概念形象描述。
指向普通整型变量的指针
int a;
int *p;
p=&a;
*p=10
首先取整型变量a的地址,接着将其赋给p,最后使用指针把10这个常量通过p指针传入变量a的空间里。
空指针和野指针
int *np=NULL;
int *p;
如上例中,np指向了’NULL’即零号地址,零号地址内部不可以被改变,也就是说使用指针无法对NULL地址内部进行赋值。
而野指针p是没有初始化之后也没有指向的指针,被称为野指针。
二级指针
即指向指针的指针。
int **pp;
pp指针可以指向指针。
指针与数组以及指针的运算
指向一维数组的指针
int arr[5]={1,2,3,4,5};
int *p;
p=arr;
因为数组名包含两重含义,一重含义是数组名,另一层含义则为数组首元素的地址,在指向一维数组的指针例子中,指针p直接指向了数组首元素的地址。那么
指向二维数组的指针
int arr[2][3]={1,2,3,4,5,6};
int (*p)[3];
p=arr;
首先解读该二维数组是由两个一维的三元素数组组成,而指针的数据类型则为’int * [3]',这里就使用了数组名是数组名的含义,所以指针p指向了整个三元素一维数组。
为何指针p需要如此定义,因为’[]‘’()‘的优先级比’*'高。
指针的运算
当指针指向一个一维整型数组,例如:
int arr[5]={1,2,3,4,5};
int *p;
p=arr;
p++;
那么当p++,则指针指向向后移动了四个字节,这四个字节意味着一个整型变量所占的空间,所以当p++或者p–时,指针会移动的单位个指向量的地址。
那么当指针指向一个二维数组时,例如;
int arr[2][3]={1,2,3,4,5,6};
int (*p)[3];//这是一个数组指针
p=arr;
p++;
同理,指针指向整个三元素数组,那么指针p+1指向地址会向后移动12个字节,即向后移动一个单位一维数组的量。
使用指针打印二维数组的例子:
#include<stdio.h>
int main(){
int arr[3][4]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int (*p)[4];
int i,j;
p=arr;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%d,",*(*(p+i)+j));//*(p+n)指向第i号一维数组,*(*(p+i)+j)指向第i号数组的第j个元素。
}
printf("\n");
}
return 0;
}
指针数组与数组指针
int (*p)[3];//这是数组指针
int *pr[3];//这是指针数组
数组指针顾名思义,即指向数组的指针,这在上文的二维数组与指针中提到,该指针指向整个数组;而指针数组,则是指针变量为元素集合起来的数组,数据类型为
‘int *pr’。
将二维数组中每一个一维数组的首地址赋给指针数组中每一个指针元素,输出二维数组:
#include<stdio.h>
int main(){
int arr[3][4]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int *p[3]={arr[0],arr[1],arr[2]};
int i,j;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%d,",*(p[i])+j);
}
printf("\n");
}
return 0;
}
这里数组的指针数组中每一个指针元素都指向一维数组的首元素地址,所以将地址+1则之移动一个元素的量,而并非上文中数组指针移动一个一维数组的量。
const
const int a=10;
const会使变量变为只读,不能再改变变量空间中所存储的值,a一直为10。
const与指针
const int *p;
int *const p;
int const*const p;
const在’*'前修饰 | const在’*'后修饰 |
---|---|
改变指针的指向,p是可以修改的 | 无法改变指针的指向,p无法修改 |
不能通过指针去修改所指向地址里存储的值,*p无法修改 | 可以通过指针修改所指向地址中的值,即,*p可以修改 |
const前后皆有则指向和赋值都无法改变。
函数
函数分为调用函数和自定义函数。
调用函数需要通过头文件调用库中的功能。而以下所说的是自定义函数。
函数的定义
函数存储类型 函数数据类型(返回值数据类型) 函数名(形式参数列表){
函数体;
返回值;
}
main(){
函数名(实际参数列表);
}
在主函数中调用自定义函数,通过实际参数列表,将值传给形式参数列表,这两个列表的变量数据类型一一对应,但实参和形参各自互不影响,各自占用各自的内存空间,可以立即为形参在另一片空间复制了实参中的值,在自定义函数结束后,形参的空间会释放掉,之后返回值再返回给主函数。
函数声明传参时的形参实际上全都为指针型变量,在64位OS下占用8Byte,在32 bitOS下占用4Byte。
自定义函数的声明
如果自定义整体写在主函数以上则不需要单独声明,但如果自定义函数在主函数以下,则需要在主函数之前声明自定义函数,自定义函数本就可以理解为一种全局变量。需要在声明语句最后加上’;'。
主函数传参
int main(int argc,char *argv[]){
...;
return 0;
}
``
此时argc意味这操作数。
打印操作字符串:
```c
#include<stdio.h>
int main(int argc,char *argv[]){
int i;
for(i=0;i<argc;i++){
printf("%s\n",argv[i]);
}
return 0;
}
*argv实际是个字符型指针数组每一个元素都指向每一条指令字符串的首元素地址,也就是如上文所述,你可以理解为一个字符型二维数组。
拾遗
printf中有表达式时,为右结合,即从右往左依次计算。