学C的第十八天【指针初阶:指针和数组、二级指针、指针数组;初识结构体:结构体的声明、结构体成员的访问、结构体传参;练习】

=========================================================================

 相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

=========================================================================

 接上期:
学C的第十七天【指针初阶:指针是什么、指针和指针类型、野指针、指针运算】-CSDN博客

=========================================================================

                

5. 指针和数组

                

指针:

指针变量就是指针变量,不是数组,指针变量的大小是 4/8 个字节,是专门用来存放地址的。

            

          

数组:

数组就是数组,不是指针,数组是一块连续的空间,可以存放1个或者多个类型相同的数据

数组的类型多种多样int arr[10] int arr[8] 数组类型就是不一样的,

前者的数组类型是 int [10] , 后者的数组类型是 int [8]

     

      

指针和数组的联系:

              

1. 数组中,数组名其实是数组首元素的地址数组名  ==  地址  ==  指针

        

2. 当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过这个特性就可以遍历访问数组数组是可以通过指针来访问的。

           

           

回顾:(两种数组名不是首元素地址的情况)

            

1. sizeof(数组名)数组名单独放在sizeof()内部,这里的数组名表示整个数组计算的是整个数组的大小

               

2. &数组名这里的数组名也表示整个数组,取出的是整个数组的地址

                   

                

 (演示代码:)

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	int* p = arr; //使用指针变量存放数组首元素地址

	for ( i = 0; i < sz; i++)
	{
		//打印数组地址
		printf("%p == %p\n", p+i, &arr[i]);
		// 使用指针变量 访问数组地址 == 使用数组下标 访问数组地址
	}

	printf("\n");

	for ( i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
		//使用指针访问 数组元素(指针和数组的联系)
	}

	return 0;
}

                   


                        

6. 二级指针

                   

概念:

                

二级指针变量就是用来存放一级指针变量指针变量(例如下图中的pp)

(解引用:二级指针解引用找到一级指针,再对一级指针解引用找到变量内容

            

              

 演示代码:
#include <stdio.h>
int main()
{
	int a = 10;

	int* p = &a; 
	//p是一级指针变量,指针变量也是变量,变量是在内存中开辟空间的,是变量就有地址

	int** pp = &p;
	//pp就是二级指针变量,二级指针变量就是用来存放一级指针变量的指针变量

	*(*pp) = 100; 
	//(*pp):解引用找到p,对p再解引用找到 a
	//二级指针解引用找到一级指针,一级指针解引用找到变量内容
	printf("%d\n", a);


	return 0;
}

              

            

二级指针的应用:

          

演示代码 -- 涉及到指针数组
//二级指针引用:
#include <stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	char arr3[] = "cuihua";

	//使用 一级指针 存放这三个字符数组的首元素
	char* parr[] = { arr1,arr2,arr3 }; //分别存放三个字符数组的首元素地址

	//使用 二级指针 存放该一级指针
	char** p = parr; //放的是一级指针的首元素地址

	return 0;
}

                        


                         

7. 指针数组

            

概念:

                      

指针数组 就是 存放指针 数组(本质就是一个数组,只不过存放的是指针)

                     

演示代码:指针数组存放字符串数组并打印
//指针数组的应用:
#include <stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	char arr3[] = "cuihua";

	//使用 指针数组 存放这三个字符数组的首元素
	char* parr[] = { arr1,arr2,arr3 }; //分别存放三个字符数组的首元素地址

	//使用 指针数组 循环打印三个首地址,再通过首地址打印三个字符串
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", parr[i]);
		// %s:通过字符串首元素地址就可以打印字符串,通过元素地址找到字符串
	}

	return 0;
}

          

               

演示代码:指针数组存放整型数组并打印
//指针数组的应用:
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	//使用 指针数组 存放这三个整型数组的首元素
	int* parr[] = { arr1, arr2, arr3 }; //分别存放三个整型数组的首元素地址

	//使用 指针数组 循环打印三个首地址,再通过首地址打印三个整型数组
	int i = 0;
	for (i = 0; i < 3; i++) //指针数组大小为3
	{
		//第一个循环:找到指针数组的元素

		int j = 0;
		for (j = 0; j < 5; j++) //整型数组大小为5
		{
			//第二个循环:找到对应数组元素地址后,再通过该地址打印整型数组内容

			printf("%d ", parr[i][j]);
			//也可以写成 *(parr[i] + j)

			//第一个【】:找到指针数组里的指针,定位到对应整型数组
			//第二个【】:定位到对应整型数组后,再用指针访问整型数组元素
		}

		printf("\n"); //打印完一个数组后换行
	}

	return 0;
}

                  

                   

总结:

                        

1. 调用指针数组访问里面元素,即指针,找到指针对应的整型数组,其用法类似二维数组的使用方式,即 指针数组名[下标][下标] ,如上图的:parr [i] [j]

               

2. 指针数组调用整型数组时,用法和二维数组的使用方式类似。但两者有区别前者数组元素在内存中是不连续存放,而后者的数组元素在内存中是连续存放。原因是:指针数组存放的元素,即指针,来自于不同的整型数组的地址

                                       

                                       

(指针初阶完)

=========================================================================

(初识结构体)

                       

                                       

1. 结构体的声明

                    

1.1:结构的基础知识

                       

结构体一些值的集合,这些称为成员变量。结构体的每个成员可以是不同类型的变量。可以说结构体一组不一定相同类型元素集合

(数组:一组相同类型元素的集合)

                      

                            

生活中的描述:

                    

人:名字 + 性别 + 年龄 + 身高 + 身份证号码 + 地址……

书:书名 + 作者 + 出版社 + 定价 + 书号……

这两者都是复杂对象不能通过内置类型直接描述和表示

所以就有 结构体描述复杂类型

                      

                     

1.2:结构的声明

语法格式:


   struct    tag        
{                      

           
            member - list;

             
 } variable - list;


struct:定义结构体的关键字

              

 tag:根据实际情况给结构体起名

               

member - list:成员变量列表(1个或者多个)

                 

variable - list:变量列表

              

(注: 大括号{} 后面有个 分号;

                  

                

演示代码:创建一个学生结构体
//结构体
#include <stdio.h>

//描述一个学生
//声明结构体类型,还没创建空间,所以成员变量没法初始化
struct Stu 
//struct:定义结构体的关键字
//tag:根据实际情况给结构体起名
{
	//member - list:成员变量列表(1个或者多个
	//成员变量:用来描述结构体对象的相关属性的
	char name[20];
	int age;
	char sex[5]; //男 女 保密,一个汉字占2个字符
} s2, s3, s4; 
//variable - list:变量列表,也可以在这创建变量,在创建类型随便创建变量
//因为是在大括号外创建的变量,所以是全局变量,开创了内存空间


typedef struct Stu
{
	char name[20];
	int age;
	char sex[5];
} Stu;
//使用 typedef 重新将 struct Stu 命名为 Stu

int main()
{
	//struct Stu 就是一个类型
	struct Stu s1; //这里创建的是局部变量
	//运用该结构体类型创建一个名为是 s1 的学生变量
	//声明一个 学生变量 后才创建了空间
	//在C语言中,没有使用 typedef 重命名的话,struct关键字 是不能省略的

	Stu s2; //使用typedef 重新命名后,直接使用重命名的名称就可以

	return 0;
}

                           

                           

1.3 结构成员的类型

                 

结构的成员可以是 标量(变量)数组指针。甚至是其他结构体

           

演示代码:
//结构成员的类型
#include <stdio.h>

struct A
{
	int a; //标量(变量)
	char arr[5]; //数组
	int* p; //指针
};

struct B
{
	char ch[10];
	struct A a; //结构体
	double d;
};

int main()
{
	return 0;
}

                           

                           

1.4:结构体变量的定义和初始化

                  

1. 结构体变量的 定义

//结构体变量的定义
#include <stdio.h>

struct A
{
	int a; //标量(变量)
	char arr[5]; //数组
	int* p; //指针
}a1; //定义种定义方式,声明类型时就创建变量,此时为全局变量

struct A a2; //创建全局结构体变量

struct B
{
	char ch[10];
	struct A a; //结构体
	double d;
};

int main()
{
	struct A a3; //在主函数中创建结构体变量

	return 0;
}

                   

             

2. 结构体变量的 初始化

//结构体变量的访问
#include <stdio.h>

struct A
{
	int a; //标量(变量)
	char arr[5]; //数组
	int* p; //指针
}a1 = {100, "lll", NULL}; //初始化方式和数组的类似

struct A a2 = {99, "hhh", NULL};

struct B
{
	char ch[10];
	struct A a; //结构体
	double d;
};

int main()
{
	struct A a3 = {.arr = "abc", .p = NULL, .a = 1}; 
	//通过 .操作符 访问成员变量,再进行初始化

	printf("%d %s %p\n", a3.a, a3.arr, a3.p);
	//使用 .结构成员访问操作符 访问结构成员变量

	struct B b1 = { "hello", {20, "qqq", NULL}, 3.14 };

	printf("%s %d %s %p %lf\n", b1.ch, b1.a.a, b1.a.arr, b1.a.p, b1.d);

	return 0;
}

                   


                        

2. 结构体成员的访问

             

相关操作符:

                 

.  结构成员访问操作符:

用于结构体变量访问结构成员,点操作符接受两个操作数 结构变量名.结构成员名

                 

               

-> 结构成员访问操作符:

用于结构体指针访问指向变量的成员
箭头操作符接受两个操作数 结构体指针->结构成员名

              

               

错误示范:传值调用
//访问
#include <stdio.h>
#include <string.h>

struct Stu
{
	int age;
	char name[20];
};

//定义一个函数,对结构体成员进行赋值
void set_stu(struct Stu t)//t 的类型就是struct Stu
{
	t.age = 20;

	//因为成员 name 是数组,数组名是首元素地址
	//t.name = "张三"; 
	//所以不能这样写,不能把 张三 放在地址上
	//应该把 张三 放在地址的空间里
	//strcpy()函数:字符串拷贝
	strcpy(t.name, "张三");
	//strcpy:把字符串拷贝到一个地方
	//把 张三这个字符串 拷贝到 t.name这个空间去
}

//定义一个函数,打印结构体变量
void print_stu(struct Stu t)
{
	printf("%s %d\n", t.name, t.age);
}

int main()
{
	struct Stu s = { 0 };

	//定义一个函数,对结构体成员进行赋值
	set_stu(s);

	//定义一个函数,打印结构体变量
	print_stu(s);
		
	return 0;
}

               

           

正确示范:使用传址调用,运用 .操作符 和 ->操作符 
//访问
#include <stdio.h>
#include <string.h>

struct Stu
{
	int age;
	char name[20];
};

//第一种方法:对地址解引用,再使用 .操作符
//定义一个函数,对结构体成员进行赋值
void set_stu(struct Stu* ps)//t 的类型就是struct Stu
{
	(*ps).age = 20;

	//因为成员 name 是数组,数组名是首元素地址
	//t.name = "张三"; 
	//所以不能这样写,不能把 张三 放在地址上
	//应该把 张三 放在地址的空间里
	//strcpy()函数:字符串拷贝
	strcpy((*ps).name, "张三");
	//strcpy:把字符串拷贝到一个地方
	//把 张三这个字符串 拷贝到 t.name这个空间去
}

//第二种方法:直接使用 ->操作符
//定义一个函数,对结构体成员进行赋值
void set_stu(struct Stu* ps)//t 的类型就是struct Stu
{
	ps->age = 20;

	//因为成员 name 是数组,数组名是首元素地址
	//t.name = "张三"; 
	//所以不能这样写,不能把 张三 放在地址上
	//应该把 张三 放在地址的空间里
	//strcpy()函数:字符串拷贝
	strcpy(ps->name, "张三");
	//strcpy:把字符串拷贝到一个地方
	//把 张三这个字符串 拷贝到 t.name这个空间去
}

//定义一个函数,打印结构体变量
void print_stu(struct Stu t)
{
	printf("%s %d\n", t.name, t.age);
}

int main()
{
	struct Stu s = { 0 };

	//定义一个函数,对结构体成员进行赋值
	set_stu(&s);

	//定义一个函数,打印结构体变量
	print_stu(s);

	return 0;
}

                   


                        

3. 结构体传参

                   

演示代码:传值传址调用
//传参
#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};

struct S s = { {1,2,3,4}, 1000 };

//方案1:结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}

//方案2:结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}


int main()
{
	print1(s); //传结构体(变量)
	print2(&s); //传地址

	return 0;
}

               

               

结论:

结构体传参的时候,要传结构体的地址

                                       

                                       

(初识结构体完)

=========================================================================

(练习:)

                       

                                       

练习:

 1. 写一个函数返回参数二进制中 1 的个数

                    

(第一种方法:%2 和 /2 取出每一位并判断)
//写一个函数返回参数二进制中 1 的个数。
//比如: 15    0000 1111    4 个 1
#include <stdio.h>

int number_of_1(unsigned int m)//要设置成无符号的,不然无法判断 负数
{
	int count = 0; //计数器
	while (m) //如果 m 不为0,说明二进制还有1
	{
		if (m % 2 == 1)//判断最低位
		{
			count++; //为1则计数器++
		}
		m /= 2; //相当于二进制去了一位
	}
	//一直判断直到m等于0,返回统计的1的个数
	return count;
}

int main()
{
	int n = 0;
	//输入:
	scanf("%d", &n);

	//定义一个计算二进制中1个数的函数
	int ret = number_of_1(n);
	
	printf("%d\n", ret);

	return 0;
}

                         

(第二种方法:使用 移位操作符 位操作符
#include <stdio.h>

int number_of_1(int m)//要设置成无符号的,不然无法判断 负数
{
	int count = 0; //计数器
	
	int i = 0;
	for (i = 0; i < 32; i++) //二进制有32位,判断32次
	{
		if ( ( (m >> i) & 1) == 1)
		//移动 i位 后再 按位与1 ,判断最低位二进制值是否为 1
		//移动后 m 的值并没有变,所以可以一直移动
		{
			count++; //是 1 则计数++
		}
	}

	return count;
}

int main()
{
	int n = 0;
	//输入:
	scanf("%d", &n);

	//定义一个计算二进制中1个数的函数
	int ret = number_of_1(n);
	
	printf("%d\n", ret);

	return 0;
}

                         

第三种方法:

n = n & (n - 1) -- 这个表达式会让n的二进制中最右边的1消失

所以在n变成0之前,能执行几次,

就说明n二进制上有几个1

#include <stdio.h>

int number_of_1(int m)//要设置成无符号的,不然无法判断 负数
{
	int count = 0; //计数器
	
	while (m)//如果 m 不为0,说明二进制还有1
	{
		m = m & (m - 1);//去掉最右边的1
		count++; //计数器++
	}

	return count;
}

int main()
{
	int n = 0;
	//输入:
	scanf("%d", &n);

	//定义一个计算二进制中1个数的函数
	int ret = number_of_1(n);

	printf("%d\n", ret);

	return 0;
}

                         


                        

2. 打印整数二进制的奇数位和偶数位

           

//获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
 #include <stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int i = 0;
	
	//打印奇数位
	printf("奇数位:");
	for (i = 30; i >= 0; i -= 2)
	//循环时是 偶数位:30 28 26.。。。
	{
		printf("%d ", (n >> i) & 1);
		//这里移位后是奇数位,按位与1 取出最低位 打印
	}

	printf("\n");
	
	printf("偶数位:");
	//打印偶数位
	for (i = 31; i >= 1; i -= 2)
		//循环时是 偶数位:31 29 27.。。。
	{
		printf("%d ", (n >> i) & 1);
		//这里移位后是偶数位,按位与1 取出最低位 打印
	}

	return 0;
}

                         


                        

3. 求两个数二进制中不同位的个数

             

//编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
//输入例子 :
//1999 2299
//输出例子 : 7
#include <stdio.h>

int number_of_1(int m)
{
	int count = 0; //计数器

	while (m)//如果 m 不为0,说明二进制还有1
	{
		m = m & (m - 1);//去掉最右边的1
		count++; //计数器++
	}
}

int main()
{
	int m = 0;
	int n = 0;
	
	//输入
	scanf("%d %d", &m, &n);

	//异或:相同为0,相异为1
	//把 m 异或 n 后,有几个相异就有几个1,再计算下二进制中有几个1即可
	int ret = number_of_1(m ^ n);
	printf("%d", ret);

	return 0;
}

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高高的胖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值