目录
前言
今天主要学习变量、结构体、内存管理和位运算符以及运算符的优先级,会各自进行C语言的基础拓展,重点是掌握局部变量、全局变量在生命周期、作用域的区别,以及关键字static、const、extern修饰下的区别;结构体的大小、使用、成员变量的访问;动态内存与静态内存;位运算符以及常见运算符的优先级问题
一、变量
1.定义规则
数据类型 变量名 变量名可以由英文字母,数字,和_组成,不可以以数字开头,实际编程工作中,一个项目常常由多个人合作完成,所以名字应该简明清晰,可采用Linux、Windows风格
2.局部变量
局部变量:定义在函数内部的变量 称为局部变量 未初始化时程序自动初始化为随机数
作用域:只在定义的函数内部有效 生命周期:随着函数调用结束而生命终止,所占用的空间自动释放。
#include <stdio.h>
void fun()
{
int x = 100;//x就是一个局部变量
}
int main()
{
printf("x is %d\n", x);
}
程序编译未通过,x未定义,这说明fun()函数内的x 与main函数的x无关。
1 两个不同函数中可以有同名变量
2 局部变量只在所定义的函数中有效
3 参数和局部变量也不能同名
4 局部变量如果没赋初值,那么值为 随机值
5 局部变量,是局部作用域,作用域在{ }之间
3.全局变量
全局变量:定义在函数外面的变量是全局变量 未初始化时程序自动初始化为0
作用域:整个函数,在程序的任何位置都可以访问,但当与局部变量重名时,优先使用局部变量 生命周期:随着整个函数结束而生命终止,
4.关键字
static const
static 修饰局部变量时,将局部变量的生命周期拓展到整个函数,但其作用域不变,但函数调用结束后,局部变量不会消亡,会继续存在
修饰全局变量时,限制只能在定义的文件中使用,内部链接,防止其他源文件引用导致错误
具有静态存储 、内部链接性质
修饰函数时,限制只能在定义函数的源文件中使用,调用时产生、调用结束时消亡。
const 外部引用说明符:作用是告诉程序,const修饰的变量在其他文件中 不可赋初值,常用修饰全局变量
extern sizeof typedef
extern 常量化 :在C语言中常量是不可修改的,当它修饰变量时,变量变成常量;修饰指针时
sizeof() 作用:求变量、数组、结构体等等的占内存空间的大小,在前面
typedef 类型重定义:起外号,例如int a; typedef int ww ww a; 等于int a 再以后的编程中会经常用到,可大大提高程序的可移植性。
5.静态变量与动态变量
静态变量与动态变量是变量的存储方式
二者区别: 1.静态变量是程序编译时就分配了内存空间且保持不变;动态变量是程序运行时才分配空间的且调用结束后会立即释放,比如实参初始化形参。
2.静态变量未初始化自动赋值0,只能赋值一次且每次调用后会被保存;动态变量未初始化自动赋值随机数,每次调用每次赋值
自动变量即局部变量,具有动态存储、代码块作用域、空链接
静态变量类型说明符:static 具有静态存储、代码块作用域、空链接 注意:外部变量虽然属于静态存储方式,但不一定是,只有static修饰的才是静态变量,根据修饰的变量类型又可分为静态局部变量和静态全局变量;未使用static修饰的静态变量具有静态、外部链接存储类型,使用的就是具有静态、内部链接类型。
练习:编写一个函数,完成下面功能,能将Hello World! 转换成Lipps Asvph!
#include <stdio.h>
void changeString(char *s)
{
int i = 0;
while(s[i] != '\0')
{
if((s[i] >= 'a' && s[i] <= 'v')||(s[i] >= 'A' && s[i] <= 'V'))
{
s[i] += 4;
}
else if((s[i] >= 'w' && s[i] <= 'z')||(s[i] >= 'W' && s[i] <= 'Z'))
{
s[i] = s[i] + 4 - 26;
}
i++;
}
}
int main(int argc, const char *argv[])
{
char a[] = "Hello World!";
puts(a);
changeString(a);
puts(a);
return 0;
}
练习:约瑟夫问题 选猴王
按顺时针方向从1到8编号。然后从1号猴子开始沿顺时针方向从1开始报数,报到m的猴子出局,再从刚出局猴子的下一个位置重新开始报数,如此重复,直至剩下一个猴子,它就是大王。设计并编写程序,实现如下功能:
(1) 要求由用户输入报的数m。
(2) 给出当选猴王的编号。
#include <stdio.h>
int main(int argc, const char *argv[])
{
int i;
int kill_post;//用来保存每一轮猴子出局的下标
int all_num = 6;//猴子总数
int start_num = 2;//从几开始数
int kill_num = 3;//数到几出局
int a[100] = { 0 };//所有的元素都是0
printf("请您输入猴子的总数 开始数号码 数到几出局:\n");
scanf("%d%d%d",&all_num,&start_num,&kill_num);
//1.将1-all_num编号赋值到数组中0-all_num-1下标对应的元素
for(i = 0; i < all_num; i++)//i == 0 1 2 3 4 5
{
a[i] = i+1;//i+1 1 2 3 4 5 6
}
//2.找到第一个出局猴子在数组中的下标
kill_post = (start_num + kill_num - 2) % all_num;
//3.循环杀猴
while(a[1] != 0)//a[1] == 0循环结束,出现猴王
{
printf("kill -----> %d\n",a[kill_post]);
//逐个向前移动一个位置,覆盖删除
for(i = kill_post; i < all_num; i++)
{
a[i] = a[i+1];
}
//删除之后,当前猴子的总数-1
all_num--;
//找到下一次,即将被杀死猴子的下标
kill_post = (kill_post + kill_num - 1) % all_num;
}
printf("monkey king is %d\n",a[0]);
return 0;
}
二、结构体
结构体:是一个 自定义的 数据类型
如何定义结构体(是一个新类型,等同于int, char, float, double,可以用结构体定义变量)
如何定义结构体变量
1.访问成员变量
如何通过结构体变量访问结构体成员 "."为成员运算符,可以访问结构体成员和赋值
练习:定义一个结构体worker,有下面成员,姓名、编号、年龄、工资(float)输入信息,并输出
#include <stdio.h>
#include <string.h>
//保存一个学生的基本信息 自己发明创造出一个 数据类型
struct student
{
char name[20];//成员变量
int age;//成员变量
int score;//成员变量
};//注意此处一定要有;号
int main(int argc, const char *argv[])
{
struct student s = {"asan",19,108};
//打印结构体中的成员变量
printf("name:%s age:%d score:%d\n",s.name,s.age,s.score);
puts("Please input name age score:");
scanf("%s%d%d",s.name,&s.age,&s.score);//s.name不加&,因为name是数组的名字,已经是地址了
printf("name:%s age:%d score:%d\n",s.name,s.age,s.score);
//定义一个结构体指针
struct student *p = &s;//p指向了s
//结构体变量访问成员变量 用 .
//结构体指针访问成员变量 用 ->
printf("name:%s age:%d score:%d\n",p->name,p->age,p->score);
return 0;
}
2.结构体指针
指向结构体的指针 定义指针变量,指向一个结构体变量 如果用指针变量访问结构体成员
值传递
如果将 打印学生信息 写成一个函数,我们就可以将学生结构体作为参数传递给函数,
#include <stdio.h>
#include <string.h>
struct student
{
char name[20];//成员变量
int age;//成员变量
int score;//成员变量
};//注意此处一定要有;号
//值传递
void setStudentInfo(struct student a)
{
printf("Please input name age score:\n");
scanf("%s%d%d",a.name,&a.age,&a.score);
}
int main(int argc, const char *argv[])
{
struct student s;//里面的成员变量是随机数
setStudentInfo(s);//值传递,不能够修改实参变量s的值
printf("值传递: name:%s age:%d score:%d\n",s.name,s.age,s.score);
return 0;
}
地址传递
#include <stdio.h>
#include <string.h>
struct student
{
char name[20];//成员变量
int age;//成员变量
int score;//成员变量
};//注意此处一定要有;号
//地址传递
void setStudentInfo(struct student *p)
{
printf("Please input name age score:\n");
scanf("%s%d%d",p->name,&p->age,&p->score);
}
int main(int argc, const char *argv[])
{
struct student s;//里面的成员变量是随机数
setStudentInfo(&s);//地址传递
printf("地址传递: name:%s age:%d score:%d\n",s.name,s.age,s.score);
return 0;
}
传递结构体数组 如果给函数传递结构体变量,尽量使用指针,好处是节省内存空间
值: a[i] == p[i] == *(p+i) == *(a+i)
地址:&a[i] == &p[i] == p+i == a+i
练习:定义一个结构体数组,且通过地址传递去输出全班基本信息
#include <stdio.h>
#include <string.h>
struct student
{
char name[20];//成员变量
int age;//成员变量
int score;//成员变量
};//注意此处一定要有;号
void showArray(struct student *p, int n)
{
int i;
for(i = 0; i < n; i++)
{//p[i]代表的是数组中的元素,访问成员变量用.
printf("%s %d %d\n",p[i].name,p[i].age,p[i].score);
//p+i代表的是地址,即指针,访问成员变量用 ->
printf("%s %d %d\n",(p+i)->name,(p+i)->age,(p+i)->score);
}
}
int main(int argc, const char *argv[])
{
//定义一个结构体数组,来保存全班的基本信息
struct student s[3] = {{"lanmao",19,100},{"taoqi",17,89},{"bienao",16,90}};
showArray(s,3);
return 0;
}
练习:写一个函数, 实现输入5个学生信息且将结构体数组传递给函数,查询其中年龄 > 25的student,并且输出该student的基本信息
#include <stdio.h>
struct student
{
char name[20];
int age;
int score;
};
void setStudentInfo(struct student *p, int n)
{
//a[i]==p[i]==*(p+i)==*(a+i)
//&a[i]==&p[i]==p+i==a+i
int i;
for(i = 0; i < n; i++)
{
printf("Please input name age score:");
scanf("%s%d%d",p[i].name,&p[i].age,&p[i].score);
}
}
void showStudentInfo(struct student *p, int n)
{
int i;
for(i = 0; i < n; i++)
{
printf("%s %d %d\n",p[i].name,(*(p+i)).age,p[i].score);
printf("%s %d %d\n",(p+i)->name,(p+i)->age,(&p[i])->score);
}
}
void findByAge(struct student *p, int n, int age)//aged代表查询大于的年龄
{
int i;
for(i = 0; i < n; i++)
{
if(p[i].age > age)
{
printf("查询结果:%s %d %d\n",p[i].name,p[i].age,p[i].score);
}
}
}
int main(int argc, const char *argv[])
{
struct student s[5];
setStudentInfo(s,5);
findByAge(s,5,25);
showStudentInfo(s,5);
return 0;
}
3.内存空间大小
计算结构体占用内存空间大小
1. 先看最长的 (2 4),最后的算出的大小一定要是最长的倍数
2. 在补齐的时候,以下面的为标准进行补齐
3. 当补齐的时候,为结构体最后一个的时候,将长度补齐为最长的倍数
三、内存管理
如果使用虚拟机模拟4G空间的话,1G Linux内核 3G用户
再3G用户中,分别时常量区、全局变量、变量区存储再栈区,申请空间函数malloc是在堆区申请的,可能会失败所以需要判断,如果成功会返回堆空间的地址,否则为NULL ;使用完全后需要手动释放free()。
1.静态内存
静态分配:在编译时确定内存的大小 数组元素个数一定是一个常量
2.动态内存
动态内存分配:在运行时再确定内存的大小 void *malloc(int size)
功能:在堆空间申请一个块连续的内存 函数参数:int size 申请空间的大小 以字节为单位
返回值:如果成功,返回申请那块那块内存空间的首地址;如果失败,返回NULL,是一个空指针
使用完后需要手动释放,释放空间
练习:定义有n个元素的动态数组,并输出
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//我想在堆空间申请5个int
int n;//用来代表学生的总人数
int i;
printf("请您输入学生的总人数:\n");//在申请空间之前,先确定学生的总人数
scanf("%d",&n);
int *p = malloc(n*sizeof(int));
//malloc申请空间,有可能失败,所以一定要在申请之后,加上条件判断
if(p != NULL)
{
printf("请输入%d个学生成绩:\n",n);
for(i = 0; i < n; i++)
{
scanf("%d",p+i);
}
for(i = 0; i < n; i++)
{
printf("%d ",p[i]);
}
printf("\n");
free(p);
}
return 0;
}
练习:学生人数由输入确定,动态分配内存,保存学生成绩,然后求出学生平均成绩
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//我想在堆空间申请5个int
int n;//用来代表学生的总人数
int i,sum = 0;
//在申请空间之前,先确定学生的总人数
printf("请您输入学生的总人数:\n");
scanf("%d",&n);
int *p = malloc(n*sizeof(int));//总人数是多少,我就申请多少个int
//malloc申请空间,有可能失败,所以一定要在申请之后,加上条件判断
if(p != NULL)
{
printf("请输入%d个学生成绩:\n",n);
for(i = 0; i < n; i++)
{
// scanf("%d",&p[i]);
scanf("%d",p+i);
sum += p[i];
}
printf("sum is %d\navg is %.2f\n",sum,(float)sum/n);
//动态内存分配需要手动申请,手动释放
free(p);//函数参数是申请空间的首地址,释放空间
}
return 0;
}
四、位运算符与优先级
代码程序存储在电脑内存中是以补码形式存在的,所以熟练使用转化方法是必须的
1.位运算符
&按位与 1代表真,0代表假,同真为真,一假即假
| 按位或 1代表真,0代表假,同假为假,一真即真
^ 异或 1代表真,0代表假,相同为假,不同为真
正数 原码 == 反码 == 补码
负数 原码 反码(原码的符号位不变,其它位取反) 补码(反码+1)
练习:求一个int a = 10类型数据 求二进制位1 的个数
#include <stdio.h>
//方法一
int getBin(int num)
{
int count = 0;
while(num != 0)//num == 0循环结束
{
if(num % 2 == 1)//先%再/
{
count++;
}
num /= 2;//等价于 num = num / 2;
}
return count;
}
//方法二
int getBin2(int num)
{//用位运算
int i;
int count = 0;
for(i = 0; i < 32; i++)
{
// if(num & (1<<i))//i = 0 1 2 3 4 ..31
if((num & (1<<i)) != 0)//此if等价于上面的写法
{
count++;
}
}
return count;
}
int main(int argc, const char *argv[])
{
int num;
scanf("%d",&num);
printf("%d中1的个数是:%d\n",num,getBin2(num));
return 0;
}
置1 用 | 置 0用 &
2. 运算符优先级
优先级口诀:
括号成员第一 全体单目第二
乘除余三、加减四
移位五、关系六
按位与、异或、按位或 八九十
总结
主要学习变量、结构体、内存管理和位运算符以及运算符的优先级,会各自进行C语言的基础拓展,重点是掌握局部变量、全局变量在生命周期、作用域的区别,以及关键字static、const、extern修饰下的区别;结构体的大小、使用、成员变量的访问;动态内存与静态内存;位运算符以及常见运算符的优先级问题