C语言基础(下)

一、结构体


1. 定义
用户自定义的数据类型,在结构体中可以包含若干不同数据类型的成员变量(也可以相同),使这些数据项组合起来反映某一个信息。

2. 格式
struct 结构体名
{
    数据类型   成员变量1;
    数据类型   成员变量2;
    数据类型   成员变量3;
};

3、结构体变量
1.概念:通过结构体类型定义的变量;
2.格式:struct  结构体名   变量名;

1)先定义结构体,再定义结构体变量
    struct 结构体名
{
    成员变量;
};
struct   结构体名   变量名;

2)定义结构体的同时,定义结构体变量;
    struct 结构体名
{
    成员变量;
}变量名;

3) 缺省结构体名定义结构体变量
struct 
{
    成员变量;
}变量名;

4.赋值
1) 定义变量时直接用大括号赋值
struct student
{
    int id;
    int age;
    float score;
    char name[32];
};
struct   student  stu={1,20,76,"zhangsan"};

2)定义变量时未初始化,然后对变量单独赋值
struct student
{
    int id;
    int age;
    float score;
};
struct   student  stu;
stu.id=2;
stu.age=56;
stu.score=20;


3)点等法赋值
struct student
{
    int id;
    int age;
    float score;
};
struct   student  stu={
            .id = 3,
            .age = 30,
            .score = 90
};

5.访问
通过.访问:结构体变量名.成员变量名
printf("%d %d %lf\n",stu.id,stu.age,stu.score);
scanf("%d %d %lf %s",&stu.id,&stu.age,&stu.score,stu.name);

1.2、重定义typedef


1) 定义结构体的同时重定义
typedef struct student
{
    int id;
    int age;
    float score;
}STU;
struct student stu;//  === STU stu;

定义的同时重新定义赋值
STU stu={1,2,30};
STU* p = &stu;

2) 先定义结构体,然后重定义
typedef  struct student  STU;
STU stu;
意思是typedef加上头文件重新定义

1.3、结构体数组


1. 概念
结构体类型相同的变量组成的数组

2. 定义格式
1) 定义结构体同时定义结构体数组
struct student
{
    int id;
    int age;
    float score;
}stu[5];

2)先定义结构体,然后定义结构体数组
struct student stu[5];

3. 初始化
1) 定义结构体同时赋值
struct student
{
    int id;
    int age;
    float score;
}stu[5]={
    {1,20,56},
    {2,21,57},
    {3,22,57},
    {4,29,57},
    {5,23,57}
};

2) 先定义结构体数组,再对数组的每一个元素分别赋值
struct student
{
    int id;
    int age;
    float score;
}stu[5];
stu[0].id=1;
stu[0].age=20;
stu[0].score=98.6;

4. 大小
结构体类型大小*元素个数
sizeof

5. 结构体数组输入输出(for循环)
struct student
{
    int id;
    int age;
    float score;
}stu[5];
int i;
for(i=0;i<5;i++)
    scanf("%d %d %f",&stu[i].id,&stu[i].age,&stu[i].score);

1.4、结构体指针


1. 概念
指向结构体变量的指针

2. 定义格式
struct  结构体名  *结构体指针名;

struct student
{
    int id;
    int age;
    float score;
}stu1,stu2;
 struct work
{
    int id;
    int age;
    float score;
}w1,w2;
struct student *p = &stu1;
struct student *p1 = &w1;//错误,结构体类型不同

3. 赋值
格式:指针变量名->成员变量名
p->id = 1;
p->age = 23;
p->score = 98;
(*p).id = 2;

4. 大小
本质是指针,4字节

5、总结
1. 不能把结构体类型变量作为整体引用,只能对结构体类型变量中的各个成员变量分别引用
2. 如果成员变量本身属于另一种结构体类型,用若干个成员运算符一级级找到最低级的成员变量
3. 可以把成员变量当成普通变量运算
4. 再数组中,数组之间是不能彼此赋值的,结构体变量可以相互赋值

1.5、结构体大小


sizeof(struct 结构体名)


1) 字节对齐原则
用结构体里面最大的数据类型的大小和4进行比较
按照字节数小的为单位开辟空间
struct std
{
Char ch;
Short a;
Double d;
};    


2)节省空间原则
减少空间浪费

二、一维数组


1. 格式
存储类型        数据类型        数组名[元素个数];

2. 访问元素
数组名【下标】,下标从0开始

3. 数组名
数组首地址

4、初始化
1) 全部初始化:
int a[5]={1,2,3,4,5}

2) 部分初始化:
int a[5]={1,2};//1 2 0 0 0
未初始化的元素值为0

3) 未初始化:随机值,需要单个元素赋值
int a[5];
a[1]=2;
a[2]=3;

5、定义空数组
1) int a[5]={0,0,0,0,0};
2) int a[5]={0};
3) int a[5]={};

6、引用
1. 先定义后引用
2. 每次只能引用一个数组元素a[i],如果想引用所有元素可以循环遍历
3. 引用时防止数组越界,虽然有时编译器不会报错
4. 打印数组元素地址用%p格式

7、数组遍历
把数组下标作为循环变量,用for循环遍历


8、数组的大小
1. 数据元素个数*数据类型大小
2. sizeof(数组名)

9、计算元素个数
1. 直接观察
2. sizeof(数组名)/sizeof(数据类型)

10、清零函数
1. bzero
#include <strings.h>
       void bzero(void *s, size_t n);
功能:将内存空间设置为0
参数:s:要清空的空间首地址
    n:字节大小
返回值:无
例:int a[10]={1,2,3};
bzero(a,sizeof(a));//从地址a开始,sizeof(a)个字节全部清空

2. memset
#include <string.h>
       void *memset(void *s, int c, size_t n);
功能:将内存空间设置为0
参数:s:要清空的空间的首地址
    c:要设置的值,设置为0;
n:字节大小
返回值:要清空的空间的首地址
例:
int a[10]={1,2,3};
memset(a,0,sizeof(a));

三、二维数组


1. 格式
存储类型  数据类型  数组名[行数][列数]

int a[2][3];

2. 访问元素
数组名[行下标][列下标];(下标从0开始)
a[0][0]:第一行第一列的元素~a[1][2];行下标、列下标都不能越界
行数可以省略,列数不能省略。
int a[2][]={1,2,3,4,5,6};

3. 数组元素个数
数组元素个数  =  行数*列数

4. 二维数组大小
二维数组大小 = 数据类型大小*行数*列数

sizeof(数组名)

5. 数组名
a:第一行的首地址,a+1:第二行的首地址

6. 初始化
1) 全部初始化:
int a[2][3]={1,2,3,4,5,6};//顺序赋值
int a[2][3]={{1,2,3},{4,5,6}};//按行赋值

2) 部分初始化:未被赋值的元素值为0
int a[2][3]={1,2,3,4};//顺序赋值  1 2 3 4 0 0
int a[2][3]={{1,2},{4,5}};//按行赋值  1 2 0 4 5 0

3) 未初始化:随机值,需要单独赋值
    int a[2][3];

7、二维数组的遍历
for循环嵌套,外层行数,内层列数
int a[m][n]={};
for(i=0;i<m;i++)//行下标
{
    for(j=0;j<n;j++)//列下标
    {
        scanf()/printf()
}
}

四、字符数组


1、字符数组的输入
char s[32]={};、


1) scanf("%s",s);
输入的字符串不能含有空格,因为scanf输入字符串遇到空格或\n都会认为字符串输入结束,空格后面的内容就不再存放到数组里面

如果需要输入空格就按以下格式输入:
scanf("%[^\n]",s);//直到遇到\n才结束


2)for(i=0;i<32;i++)
{
    scanf("%c",&s[i]);
}


3)gets
 char *gets(char *s);


功能:从终端获取字符串
参数:s:目标字符数组的首地址
返回值:目标字符数组的首地址
gets(s);//gets在输入时不关心数组越界问题,使用时会报警告
 

2、输出
1. printf("%s\n",s);


2. for(i=0;i<32;i++)
        printf("%c ",s[i]);


3.puts
int puts(const char *s);
功能:向终端输出字符串
参数:s:要输出字符数组的首地址
返回值:输出字符的个数
puts(s);

五、计算字符串实际长度

1. for循环遍历数组,直到\0为止
for(n=0;buf[n] != '\0';n++);

2. strlen
#include <string.h>
       size_t strlen(const char *s);
功能:计算字符串的实际长度(不包括\0)
参数:s:要计算的字符串的首地址
返回值:字符串的实际长度
例子:
char a[32]="hello";
int num = strlen(a);//num = 5
sizeof(a);//32

sizeof和strlen的区别?
1. sizeof是关键字,strlen是函数
2. sizeof是计算数据所占空间大小,strlen计算字符串的实际长度
3. sizeof计算包括\0,strlen计算不包括\0;计算字符串长度时(元素个数省略情况下),sizeof比strlen大1

六、冒泡排序

n个数:比较n-1轮,每轮交换的次数从n-1次开始依次递减
算法实现:
#define N 5
int a[N]={};
for(i=0;i<N;i++)
    scanf("%d",&a[i]);
for(i=0;i<N-1;i++)//轮数
{
    for(j=0;j<N-1-i;j++)//比较的次数
    {
        if(a[j] > a[j+1])//交换
           {
              temp = a[j];
              a[j] = a[j+1];
              a[j+1] = temp;
            }
    }
}

七、指针

1. 概念:
地址:内存中每个字节单位都有一个编号(门牌号)
指针:指针就是地址
指针变量:用于存放地址的变量就叫做指针变量
2. 格式:
存储类型        数据类型     *指针变量名
int *p;//定义了一个指针变量p
例子:
int a=5;
int *p=&a;
char b='v';
char *q = &b;
3. 指针操作符:
&:取地址符:取变量的地址
 * :取内容:取地址里面的内容
*&a = a;   // *和&是互逆运算
&*a//错误(原因:运算符优先级)

1.2、初始化

1. 将普通变量的地址赋值给指针变量
int a=10;
1) int * p = &a;//定义的同时赋值
2) int *p = NULL;
p = &a;//先定义后赋值
printf("%d %d\n",a,*p);//打印a的值 10 10
printf("%p %p\n",&a,p);//打印a的地址

*p = 3;//可以通过*p改变指向的内容
printf("%d %d\n",a,*p);//打印a的值 3 3
printf("%p %p\n",&a,p);//打印a的地址

2. 将数组的首地址赋值给指针变量
char s[10]="hello";
char *p = s;//指针指向数组首地址,即指向'h'
3. 将指针变量里面保存到地址赋值给另一个指针变量
float a=1.3;
float *p = &a;
float *q = p;


1.3、指针运算

(1) 算术运算
    char s[32]="hello";
    char *p = s;
    p++;//指针向高地址方向移动一个数据单位,指针指向发生变化
    p--;//指针向低地址方向移动一个数据单位,指针指向发生变化

关系运算
>    <    ==    !=
指针之间关系运算比较的是它指向地址的高低
指向高地址的指针大于指向低地址的指针
char s[10]="hello";
char *p1 = &s[1];
char *p2 = &s[3];
p1 < p2;
注意:指向不同类型的数组指针关系运算没有意义,指向不同区域的指针关系运算也没有意义
(同一个数组间进行比较)

1.4、指针的大小

sizeof(指针变量名)
int a=5;
int *p1 = &a;//4
short b = 2;
short *p2 = &b;//4
double c = 3.333;
double *p3 = &c;//4
double *p4 = NULL;//4

32位操作系统:指针为4字节
8位16进制表示  ,4字节
64位操作系统:指针8字节
16位16进制表示,8字节

八、指针修饰

1. const常量化

1) const int a=10;
int const a=10;
a=20;//错,a的值不能变,可以通过指针间接修改
int *p = &a;
*p = 20;//可以

2)const int *p;//修饰*p,指针指向的内容不能修改,指针的指向可以修改
int const *p;
    int a=10;
    const int *p=&a;
a. )*p =20;//错误,因为*p被修饰
b. )int b=20;//正确
p=&b;

int * const p;//修饰p,指针的指向不能被修改的,指针指向的内容可以修改
int a=10;
int b=20;
int *const p=&a;
a) *p = 20;//正确
b) p=&b;//错误,p被const修饰,所以不能修改

2. void

void a;//不允许修饰变量
void *p;//p是一个任意类型的指针
使用场景:函数参数或函数返回值
注意:通过void类型指针进行取内容时,需要对地址进行强转
转换方式:void *p=NULL;   强转(int *)p    取内容:*((int *)p)

九、二级指针

二级指针:存放一级指针的地址
格式:存储类型    数据类型     **指针变量名

int  a=10;
int *p = &a;
int **q = &p;

访问a的值:
a  *p  **q
访问a的地址:
&a  p   *q
访问p的地址:
&p   q

十、指针和数组

1. 指针和一维数组

访问数组元素a[i]的值:
直接访问:a[i]  *(a+i)
间接访问:p[i]  *(p+i)
访问数组元素a[i]的地址:
直接访问:&a[i]  a+i
间接访问:&p[i]  p+i

单目运算符从右向左运算
*(p++);//1
(*p)++;//打印出来1,实际上第一个数变成2;
++*p;//打印出来是2,自加完之后的值
++(*p)//同上
*++p;//先移动再取值,2
*(++p)同上

2. 指针和二维数组

int a[2][3]={1,2,3,4,5,6};//a:数组名,表示第一行首地址,a+1:第二行首地址
在a前面加*,表示将行地址降级成为列地址
*a://第一行第一列的地址
*a+1//第一行第二列的地址
*(a+1)//第二行第一列的地址
*(a+1)+1//第二行第二列的地址

直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存放变量地址的变量去访问变量(通过指针访问)

十一、数组指针

定义:本质是指针,指向的是数组(又称为行指针)
格式:存储类型  数据类型 ( * 指针变量名) [列数]
int a[2][3]={1,2,3,4,5,6};
int (*p)[3] = a;
访问a[i][j]地址:
*(p+i)+j     p[i]+j
访问a[i][j]内容:
*(*(p+i)+j)     *(p[i]+j)

十二、指针数组

1) 用于存放普通变量的地址
int a=10,b=20,c=30;
int *p[3]={&a,&b,&c};
访问b的值:
*p[1]     *(*(p+1)) 
访问b的地址;
p[1]    *(p+1)


2) 用于存放二维数组的每行第一个元素的地址(列地址)
int a[2][3]={1,2,3,4,5,6};
int *p[2]={a[0],a[1]};
访问a[1][2]的值:
*(p[1]+2)  *(*(p+1)+2)
访问a[1][2]的地址:
p[1]+2    *(p+1)+2
sizeof(p)=8;//因为p中包含了两个指针

3.用于存放字符串
char *p[3]={"hello","world","hqyj"};
打印"world"字符串
printf("%s\n",p[1]);
printf("%s\n",*(p+1));
打印’d’这个字符:
printf("%c\n",*(p[1]+4));
printf("%c\n",*(*(p+1)+4));
//printf("%c\n",p[1][4]);


4)命令行参数
int main(int argc, char const *argv[])
{
    printf("%s\n",argv[0]);
}
argv:就是一个指针数组,里面存放的是命令行传递的字符串
argc:表示argv指针数组里面存储数据的个数,即命令行传递字符串的个数

十三、共用体

1. 格式:
union  共用体名
{
    成员列表;

};

2. 定义共用体变量
union 共用体名   变量名;

union  val
{
    int a;
    char ch;
};
union val v;
v.a=10;
v.ch = 'a';

3. 特性:
1) 共用体成员共用同一块地址空间
2) 赋值顺序以最后一次赋值为准
3) 共用体的大小为成员中类型最大的数据的大小
struct stu
{
    int num;
    char ch;
};

使用共用体测试大小端:
union val
{
    int num;
    char ch;
};
int main()
{
    union val a;
    a.num = 0x12345678;
    if(a.ch == 0x78)
        printf("小端\n");
    else
        printf("大端\n");
    return 0;
}

十四、枚举

1. 定义:用户自定义数据类型,可以用于声明一组常数

2. 格式:
enum 枚举名
{
    value1,
    value2,
    value3,
。。。。。

};
未赋初值,第一个常数会默认为0,依次加一。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值