C语言入门学习 第二天

对于指针这一块还写入了函数部分,函数部分可以先不用看,等到第三天函数学完之后再来看函数部分。把函数写入指针可以使指针更加具体,或者你可以把第二天的补缺和数组看完后直接看第三天的函数内容,之后再看第二天的指针。

目录

前言

一        补缺

二        数组

三        字符串处理函数

四        二维数组

五        指针•指针与指针变量

六        指针•指针变量指向普通变量、数组、字符串、函数       

七        指针•指针数组与二级指针

八        指针•指针数组作main函数的形参

九        指针•动态内存分配



前言

        昨天学习了几个知识点,今天的目标是把昨天的一些知识点进行补充,并且学习新的内容数组和指针。


一        补缺

1.赋值赋值符:赋值运算的左边必须是一个lvalue,变量名就是lvalue。

...            ...
int a;         int a;
a=5;           5=a;
.../*右边是常量赋给变量名(常量lvalue不是),编译就会出现
error:lvalue required as left operand of assignment*/

2.复合的赋值运算符:就是把算数运算符和赋值运算符结合到一起。

a=a+1    a+=1
a=a-2    a-=2
a=a*3    a*=3
a=a/4    a/=4
a=a%5    a%=5//举例说明

3.自增自减运算符:变量加一或者减一并赋值给本身的时候使用。同时只能作用于变量不能作用与常量或者表达式。,

i=i+1    i++ ++i
i=i-1    i-- --i
//i++是先使用变量里的值,然后自身(以前变量的值)在加一
//++i是先使变量里的值加一,然后在使用变量里的值

4.逗号运算符:逗号表达式的运算过程为从左往右逐个计算表达式 。逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。也要注意逗号在c语言中不一定是逗号运算符,有时候也只是分隔符而已。

语法:表达式1,表达式2,....,表达式n(其中表达式1,表达式2,...,前面的只计算最后的值是表达式n的值)

...
i=1;
i=2;        i=1,i=2,k=3;
k=3;
.../*左边是三个语句,右边是一个语句为整体
举例说明:a=(b=3,(c=b+4)+5)
先将变量b赋值给3,然后变量c赋值为b+4就是7,接下来把c的值加上5,
最后赋值给a,得到变量a的值是12.*/

5.条件运算符:在C语言中我们学了,单目双目运算符,而还有一个三目运算符就是条件运算符。

语法:exp1? exp2: exp3;  (exp1是条件表达式,如果结果为正返回exp2,如果条件为假返回exp3)

if(a>b)
    max=a;
else
    max=b;    //可以写成
max=a>b?a:b

6.goto语句:goto语句就跑到标签那,直接跳过其他程序往后执行。不建议使用会破会代码逻辑性。

语法:goto + 标签;


二        数组

1.数组:对于类型一致,数量庞大的数据可以用数组,数组就是存储一批同类型数据的地方。定于数组的语法如下所示:

语法:类型   数组名[元素个数]

例如:int   a[6];占用了整型四字节共三个就是24个字节。char  b[24];字符型一个字节共24个就是24个字节。double  c[3];浮点型八个字节一共三个就是24个字节。所以这三个占用空间一样。

2.数组不能动态定义:数字是在程序运行过程中去修改数组空间大小,也就是数数组是不能被动态定义的。数组的定义需要写在程序的最开头位置,在定义一个循环变量的是后要往中间写的话要在编译的时候加上std=C99。(现在新的C99标准已经可以被动态定义)

3.访问数组中的元素语法:数组名[下标],下标为零是访问数组第一个元素,同样访问下表n是访问数组n+1个元素。同时要注意如果在int  a[5]这个数组中,访问a[5]中的元素就是报错,因为第五个元素的下表是a[4],这就是数组越界,数组越界是C语言中最常见的bug,数组会经常访问到外面的元素去,C语言不会去报错因为C语言不会去检查你的数组越界问题,里面没有需要需求访问的东西可能会报错。

4.循环跟数组的关系:我们经常要求使用循环来访问数组。所以for循环一般就是i=0来定义。

int a[10];
for(i=0;i<10;i++)
{
a[i]=i;
}

5.数组的初始化:如果需要在定义的同时需要给他进行赋值称之为数组的初始化。另外sizeof()后跟数组名的话,编译出来的是数组元素占用内存的大小。

  • 将数组中所有元素初始化为0,可以这么写: int a [10]=[0};1//事实上这里只是将第一个元素赋值为0,若后面没有赋值一般默认为0。
  • 如果是赋予不同的值,那么用逗号分隔开即可:int a [10]={1,2,3,4,5,6,7,8,9,0};
  • 你还可以只给一部分元素赋值,未被赋值的元素自动初始化为0: int a [10]={1,2,3,4心5,6};//表示为前边6个元素赋值,后边4个元素系统自动初始化为0。
  • 有时候还可以偷懒,可以只给出各个元素的值,而不指定数组的长度(因为編译器会根据值的个数自动判断数组的长度): int a []={1,2,3,4,5,6,7,8,9,};
  • C99增加了一种新特性:指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为0:int a[10] = {[3] = 3,[5] = 5,[8] =[8]};


三        字符串处理函数

1.字符数组:C语言一般是没有字符串类型的,那么存放和表示字符串就一共由两种方式。第一种方式字符串常量:"XXX"缺点是无法改变,第二种方式字符类型的字符数组最后得添加一个表示字符串的结束符/0.

int main()//第一种写法
{
     char i[5];
     
    i[0];
    i[1];
    i[2];
    i[3];
    i[\0];
}
int main //第二种写法
{
//初始化字符数組的每个元素
char str1[10]={' F ',' i '' s ',' h ','℃','\0'};
//可以不写元素的个数,因为编译霖会自动计算
char str3[]={' F ',' i ,' s ',‘ h ',‘ C ',‘\0'};
//使用字符串常量初始化字符数组
char str4[]={" Fishc "};
//使用字符串常量初始化,可以省略大括号 
char str5[]=" FishC ";
}

 2.字符串处理函数:以下是我们学习的处理字符串函数(我们可以直接使用标准库函数(在一些网站上找))

  • 获取字符串的长度:strlen(sizeof是字符串的尺寸比strlen多了一个\0)
  • 拷贝字符串:strcpy和strncpy。a.(strcpy(X1,X2)X1存放字符串的目标字符串,X2拷贝的源字符串,拷贝是应该限制源字符串的长度,确保目标字符串在拷贝完之后不会发生溢出。)。b.和strcpy函数一样,strncpy(dest, src, n) 函数将拷贝源字符串的 n 个字符到目标数组中。如果源字符串的长度小于 n,那么就用 '\0' 填充额外的空间。如果源字符串的长度大于或等于 n,那么只有 n 个字符被拷贝到目标数组中(注意:这样的话将不会以结束符 '\0' 结尾,要在后面给最后一个目标字符串数组赋一个'\0')。
  • 连接字符串:strcat和strncat。a.将一个字符串连接到另一个字符串的后面,我们也叫做拼接。strcat将源字符串拷贝并连接到目标数组存放的字符串后边,此过程将覆盖第一个参数的结束符 '\0'。b.strncat 函数用于拷贝源字符串中的 n 个字符到目标数组的字符串后边,并在末尾添加结束符 '\0'。如果源字符串的长度小于 n,那么不会像 strncpy 函数那样使用 '\0' 进行填充(但结束符 '\0' 还是有的)。另外,目标数组中的原有的字符串并不算在 n 中。
  • 比较字符串:strcpt和strncpt。a.trcmp 函数用于比较两个字符串。该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,直到发现两个字符不相等或抵达结束符('\0')为止。b.strncmp 函数用于比较两个字符串的前 n 个字符。该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,发现两个字符不相等或抵达结束符('\0')为止,或者前 n 个字符完全一样,也会停止比较。(其中相等返回值是0,否则为大于或小于)


四        二维数组

二维数组通常可以我们叫矩阵,可以看成行和列的形式。

1.二维数组的定义:类型  数组名[常量表达式][常量表达式]  //一维和二维

举例:int a[4][5];就是4行5列 4*5也可以是字符型,浮点型。在内存中以线性存储。

 2.二维数组的访问:

数组名[下标][下标]
 a [0][0];//访问 a 数组中第1行第1列的元素
 b [1][3];//访问 b 数组中第2行第4列的元素
 c [3][3];//访问 C 数组中第4行第4列的元素
/*同样需要注意下标的取值范围,以防止数组的越界访问。
一比如 int a [3][4],其“行下标”的取值范围是日~2,“列下标”的取值范围是0~3,超出任何一个下标的访问都是越界访问。*/

3.二维数组的初始化

//由于二维数组在内存中是线性存放的,因此可以将所有的数据写在一个花括号内:
 int a [3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
//为了更直观地表示元素的分布,可以用大括号将每一行的元素括起来:
 int a [3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
//或者写成
 int a [3][4]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
}
//二维数组也可以仅对部分元素赋初值(一个括号代表一列所以说下例子是每行的第一个数):
 int a [3][4]={{1},{5},{9}};
//如果希望整个二維数组初始化为日,那么直接在大括号里写一个日即可:
 int a [3][4]={0};
/*C99同样增加了一种新特性:指定初始化的元素。这样就可以只对数组中的某些指定
元素进行初始化赋值,而未被赋值的元素自动初始化为日:*/
 int a [3][4]={[0][0]=1,[1][1]=2,[2][2]=3};

另外下图程序中矩阵的转置是改变for循环中行数和列数也就是4和5,再把a[i][j]换为a[i][j]。


五        指针•指针与指针变量


指针使非常重要的具体内容是:

指针与指针变量
指针变量指向普通变量、数组、字符串、函数
指针数组与二级指针
返回指针的函数
指针数组作 main 函数的形参
动态内存分配

我们在程序中定义一个变量,程序在编译中系统就会根据变量的类型在系统中分配对应强度的空间,我们在访问的时候是通过变量名来访问内存。内存的最小索引单元是一个字节可以把内存想象成一个超级大的字符数组,数组有索引下标(数组名[下标]数组的索引下标是从零开始的)内存也是我们把它称为地址,每个地址可以存放一个字节的数据,所以要存放一个整型变量就需要四个存储单元(每个存储单元有一个地址),而变量名是方便程序的使用来定义的,只有程序员和编译器知道,编译器又知道具体的变量名和地址,当我们读取某个变量的时候编译器就会通过变量名所在的地址并根据变量的类型读出相应范围的数据。

 1.指针和指针变量:通常我们所说的指针就是地址的意思,C语言里有专门的指针变量来存放指针,不同的是普通变量里面存放的是数据,指针变量存放的是地址。指针变量和变量一样也有类型它的类型就是存放的地址指向的数据类型。

 在上图中定义了两个字符变量pa,pf。pa所在的内存地址是11000,在上图中表可知一个指针在编译系统里占用四个字节(其他的不一定)存放10000,而在右表10000是一个地址,里面存放F。也就是说中间表的地址就是指针,而存放地址的变量就是指针变量(也就是中间表中的存放的值)。

定义指针变量语法(定义):类型名 *指针变量名(类型是指针变量中存放的地址指向的内存单元的数据类型,也就是说在上图中pa存放的是a的地址变量a是一个字符'F',所以pa就是指针变量定义的话就是一个字符类型的指针)。不同数据类型所占的内存空间不同如果说指定错误了,那么访问指针变量指向的数据时就会出错。

char *p;//定义一个指向字符型的指针变量

int *p;//定义一个指向整形的指针变量
定义格式中*表示变量一个指针变量
p为一个指针变量,指向的变量是整型

严格来说,一个指针时一个地址值,是不能变化的值。而一个指针变量的内容是可以改变的,可以是不同的地址值。为了叙述方便我们把经常把指针变量简称为指针。因此,当指针变量得值是某个内存单元的地址是,可以把这个变量称为某个内存单元的指针。例如:当p指向变量i时,可以说p时i的指针。

2.指针的使用:定义变量,初始话指针变量,使用指针变量。

定义变量:类型名 *指针变量名

初始话指针变量:指针变量和其他变量一样,如果指针变量没有被赋值,指针变量的值将是不定的,所以如果想让p的内容是i的地址,必须做一个取地址用算。

int i;//定义整型变量
int *p;//定义指针变量p
p=&i;//p指向i

上端程序中开头俩句是定义语句,如果只有这两个句子,i和p是没有关系的。此时p的值是不定的,至于执行了第三句,才可以是p指向i。

使用指针变量:通过“*p”则可引用指针变量p指向的普通变量a里存放的内容。

#include<stdio.h>

int main(void)
{
    int a;
    int* p;
    p = &a;
    printf("输入一个整数\n");
    scanf_s("%d", p);  //等价于 scanf_s("%d", &a);

    printf("a=%d\n", a); //普通变量a中的数值
    printf("*p=%d\n", *p); //*p表示指针变量p指向的普通变量a中存储的数值

    return 0;
}

另外还有指针变量没有指向,int *p= NULL;NULL是在标准库中定义的一个常量,对于指针它表示0。NULL是一个不指向任何内存位置的值

3.取地址运算符和取值运算符:指针运算符有两个,取地址运算符&和取值运算符*,使用取值运算符时可以存取所指的存储单元的内容。都为单目运算符。

//如果需要获取某个变量的地址,可以使用取地址运算符(&):

 char *pa=&a;//scanf()时用到&,指针变量获取变量a的地址(称为简介访问)
 int *pb=&f;//把a,b的地址把它存放到对应的指针变量中去

请看下例:最后一句是使用了取值运算符,含义是3赋值给p指向的储存单元i,结果是i变成了3。

int i;//定义整型变量
int *p;//定义指针变量p
p=&i;//p指向i
*p=3;//使i的内容为3

这里需要注意取值运算符和定义指针时用的符号时同一个符合,属于符号的重用。不同地方有不同的意义,定义的时候表示定义一个指针变量在其它的地方表示取指针变量指向的那个变量的直接通过变量访问变量的来我们称为直接访问。通过指针变量指针里存放的地址,通过这个地址指向的那个值叫简介访问, 取值运算符又时又称间接运算符(这段话基本也正确其实可以一看)。

举例:

#include<stdio.h>
int main()
{
    char a='F';
    int f=123;

    char *pa=&a;//取地址
    int *pb=&f;
    
    printf("a=&c\n",*pa);//取值操作符,取变量里地址操作的值
    printf("b=&d\n",*pb);   

}
a=F
b=123
#include<stdio.h>
int main()
{
    char a='F';
    int f=123;

    char *pa=&a;//取地址
    int *pb=&f;
    
    printf("a=&c\n",*pa);//取值操作符,取变量里地址操作的值
    printf("f=&d\n",*pb);   

    *pa='C';//通过指针间接访问
    *pa+=1;
    
    printf("now,a=&c\n",*pa);
    printf("now,f=&d\n",*pb);   
}
a=F
b=123
now a=C
now b=123

避免访问未初始化的指针,指针变量会随机分配地址。


六        指针•指针变量指向普通变量、数组、字符串、函数       

1.指针指向普通变量(这个小结用程序来说明)

a.指针指向整型变量

#include<stdio.h>

int main(void)
{
    int a, b,c;
    int *p1, *p2;  /*定义整型指针变量p1,p2*/
    a = 150; b = 10;
    p1 = &a;    /*初始化p1,使其指向整型变量a*/
    p2 = &b;   /*初始化p2,使其指向整型变量b*/
    c = *p1 + *p2;
    printf("a=%d,b=%d\n", a, b); /*输出变量a和b的值*/
    printf("*p1=%d,*p2=%d\n", *p1, *p2); /*通过*p1,*p2,引用其指
    向的整型变量a和b的内容,输出效果与上一句比较后,是一样的*/
    printf("\nThe sum is %d\n", c);
    return 0;
}

b.整型指向浮点型变量

#include<stdio.h>

int main(void)
{
    float f1, f2;
    float* p1, * p2; /*定义浮点型指针变量p1,p2*/
    p1 = &f1;  /*初始化p1,使其指向普通变量f1*/
    p2 = &f2;  /*初始化p2,使其指向普通变量f2*/
    printf("请输入两个小数,用空格隔开\n ");
    scanf_s("%f%f", p1, p2);  /* scanf_s函数需要变量的地址列表,而p1
    和p2里存放的就是变量f1和f2的地址,等价于scanf_s ("%f,%f",&f1,&f2);*/
    printf(" f1=%10f,  f2=%10f\n", f1, f2);
    printf("*p1=%10f, *p2=%10f\n", *p1, *p2); /*输出由p1和p2所指向的变量内容,结果同上句*/

    return 0;
}

c.整型指向字符变量

#include<stdio.h>

int main(void)
{
    char c1, c2;
    char* p1, * p2; /*定义字符型指针变量p1,p2*/
    p1 = &c1;  /*初始化p1,使其指向普通变量c1*/
    p2 = &c2;  /*初始化p2,使其指向普通变量c2*/
    printf("请输入两个字符,不用间间隔符\n ");
scanf_s("%c%c", p1,sizeof(char), p2, sizeof(char));  /*也可以使用*p1=getchar();*p2=getchar();*/
    printf(" c1=%c, c2=%c\n", c1, c2);
    printf("*p1=%c,*p2=%c\n", *p1, *p2); /*输出由p1和p2所指向的变量的内容,与上一句结果一样*/

    return 0;
}

 d.指向普通变量的指针变量做参数

函数的形参不仅可以是整型、实型、字符型等数据,还可以是指针类型。

2.数组

a.指向一维数组

数组名:是数组第一个元素的地址。

指向数组的指针:

char *p;
p=a;//语句一
p=&a[0];//语句二

指针的运算:

  • 当指针指向数组元素的时候,我们可以对指针变量进行加减运算,这样做的意义相当于指向距离指针所在位置向前或向后第 n 个元素。
  • 对比标准的下标法访问数组元素,这种使用指针进行间接访问的方法叫做指针法。
  • 需要郑重强调的是: p +1并不是简单地将地址加1,而是指向数组的下一个元素。

 

  •  指针变量指向一维数组的元素
  • 指针变量指向一维数组
  • 指针变量指向一维数组的指针变量做参数

b.指向二维数组 

  •  指针变量指向二维数组的元素
  • 指针变量指向二维数组
  • 指针变量指向二维数组的指针变量做参数

1) 指针变量指向二维数组的元素

 int a[3][4];
 int* p; //定义指针变量
 p = &a[i][j];//初始化,或者p=*(a+i)+j或者a[i]+j
 *p; //引用二维数组元素
 *p++; //逐一引用二维数组元素,“p++”会使指针变量移动到二维数组的末尾,编程时需注意。

 2)指针变量指向二维数组

int a[3][4];
int(*p)[4];  //定义行指针变量
二维数组的行指针变量移动到下一行的起始地址;
通过该行指针变量引用二维数组的的元素值。

3)指针变量指向二维数组的指针变量做参数

    形参              实参
列指针变量            行地址
行指针变量            列地址

七        指针•指针数组与二级指针

1.指针数组与二级指针

a.指针数组

指针数组的定义形式为:类型说明符 *数组名[数组长度]
指针数组比较适合用来指向若干个字符串,在静态初始化时,节省空间。

b.指向指针变量的指针变量

 int ** p;   
//定义了一个二级指针变量,(*p)是一个指针变量,*(*p)是指向整型指针变量的指针变量
 

 

 2.指针变量指向函数(学完第三天的函数之后在学)

a.指针变量指向函数的使用说明:定义 初始化 使用
b.指针变量指向同型函数:同型函数是有相同函数首部的函数定义了指向函数的指针变量时,它可以指向任意同型函数。用函数名调用函数,只能调用所指定的一个函数,而通过指针变量调用函数比较灵活。

c.用指向函数的指针变量作函数参数

指针•返回指针的函数

定义返回值是指针的函数的一般形式为:

类型说明符* 函数名(形参表)

{

        …… /*函数体*/

}

八        指针•指针数组作main函数的形参

main函数的函数首部可写为:main (int argc,char *argv[])
C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。其中argc是英文单词argument count(参数个数)的缩写,argv是英文单词argument value(参数值)的缩写。Argc表示形参的个数,argv是字符指针数组,用来指向argc个字符串。

九        指针•动态内存分配

a.动态内存的分配:对内存的动态分配是通过系统提供的库函数来实现的,主要有4个函数
malloc(memory allocate)
calloc(continue allocate 连续内存分配)
free(释放内存)
realloc(reallocate 再分配)
这4个函数的声明在stdlib.h头文件中。

b. 怎样建立动态内存的分配:就是使用这四个函数。

c.void指针类型:C99标准把以上malloc,calloc,realloc函数的基类型定为void类型,这种指针称为无类型指针即不指向哪一种具体的类型数据,即仅提供一个纯地址,而不能指向任何具体的对象。在使用时,可以对申请的void地址进行强制类型转换,也可以有系统自动转换。例如
int* pt;
pt = (int *)malloc(100);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值