C语言 | 第十一章 | static 日期函数 数学函数

P 100 变量作用域基本规则 2023/1/9

一、基本介绍

概念:所谓变量作用域(Scope),就是指变量的有效范围。

  1. 函数内部声明/定义的局部变量,作用域仅限于函数内部
#include<stdio.h>
void sayHello() {
	char name[] = "tom";   //  这里name就是局部变量,作用域仅限于在sayHello函数中!!!
	printf("hello %s \n", name);
}
void main() {
	sayHello();
	//这里我们不能使用到sayHello的name变量
		printf("name= %s", name); //这里将提示,没有定义name
}
  1. 函数的参数,形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用局部变量(编译器使用就近原则)
#include<stdio.h>
int n = 20;    // 函数外部定义的变量,就是全局变量(作用域在整个程序中都可以使用)

// 函数的形参,会被视为f10的局部变量
// 说明:当局部变量与全局变量同名时,以局部变量为准(就近原则)
void f10(int n){
	printf("\nn=%d",n);
}

void main() {
		f10(10);  // 当函数外定义了同名变量,依然还是输出10
	getchar();
}
  1. 在一个代码块,比如 for / if中 的局部变量,那么这个变量的的作用域就在该代码块。
void main() {
	int i = 0;
	for (i = 0; i < 10; i++){
		int k = 90; //k 的作用域在for代码块中
		printf("i=%d k = %d\n", i,	 k);
	}
	printf("k=%d", k); // 这里不能使用for中定义的k变量,因为k的作用域范围只在for循环中。
	getchar();
}
  1. 在所有函数外部定义的变量叫全局变量,作用域在整个程序有效。(通常将全局变量放在头文件中)
myFun.h  // 在myFun.h中定义了全局变量,使用只需要引入头文件就行,但是只能引入一次,引入多次会重复定义。
	double money = 1.1; //定义了全局变量
hello.c
#include "myFun.h";  // 这里引入即可

P 101 变量初始化注意事项 2023/1/9

一、初始化注意事项

  1. 局部变量,系统不会对其默认初始化,必须对局部变量初始化后才能使用,否则,程序运行后可能会异常退出。
#include<stdio.h>
void main(){
	// 局部变量,系统不会对其初始化。
	// 必须对局部变量初始化后才能使用,否则,程序运行后可能会异常退出。
	int i;
	printf("%d",i);
}
  1. 全局变量,系统会自动对其初始化,如下所示:
数据类型初始化默认值
int0
char‘\0’
float0.0
double0.0
pointer 指针NULL
#include<stdio.h>
int a;
float f;
double d1;	 // 以上定义为全局变量

void main() {									// 全局变量会初始化,默认输出下面的值
	printf("\na=%d f=%.2f d1=%.2f", a, f, d1);  // a = 0,f = 0.00,d1 = 0.00
	getchar();
}
  1. 正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量会导致一些在内存位置中已经可用的垃圾值。(异常退出可能就是指向了一些垃圾值)

P 102 作用域细节和内存布局图 2023/1/10

一、注意事项和细节

  1. 全局变量(Global Variable)保存在内存的全局存储区中,占用静态的存储单元,它的作用域默认是整个程序,也就是所有的代码文件,包括源文件(.c文件)和头文件(.h文件)。【c程序内存布局图!!!----非常重要】

image-20230110104257993

  1. 局部变量(Local Variable)保存在栈中,函数被调用时才动态地为变量分配存储单元,它的作用域仅限于函数内部。【内存布局分析 】

  2. C语言规定,只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量。

int a;
float f;
double d1;	 // 以上定义为全局变量

void f20(){
	int num = 90;   // 函数,定义就是局部变量
	printf("a=%d",a);  //在局部里面去使用这个a,相当于是从小的范围里面像大的范围里面去找
								// 但是外面是不可以使用局部变量里面的num
}
  1. 同一个作用域,变量名不能重复,在不同的作用域,变量名可以重复,使用时编译器采用就近原则.
void f20(){ 
	int num = 90;  // 函数,定义就是局部变量
	int num = 89;   //  在同一个作用域里面是不能重复定义
	printf("a=%d",a);
}

void f30(){  // 我在f30里面在定义一个num,在不同作用域是不会冲突的
	int num = 100;
}
  1. 由{ }包围的代码块也拥有独立的作用域(例如使用for、if前面讲解过)。
void f30(){
	int num = 100;
	if(1){
	int num = 200;  // 这里再次定义同样不会报错
        			// 对于if,外面的就相当于全局变量,里面的是局部变量,里面的是小范围,外面的是大范围,采用就近原则。
	}
}

P 103 作用域课堂练习题 2023/1/15

一、题目练习

题目一:下面的代码输出什么内容?

#include<stdio.h>

double price = 200.0; //全局变量
void test01() {
	printf("%.2f \n", price); //函数里面没有定义, 就是 全局变量 : 200.0
}
void test02() { //编译器采用就近原则
	price = 250.0; // 如果把 这句话 改成 double price = 250.0 , 输出有什么变化?
//  double price = 250.0; 
// 定义之后就是局部变量了,跟全局变量没有关系。
	printf("%.2f \n", price); // 250.0
}
void main() {//main函数
	printf("main price=%.2f \n", price); // 200.00
	test01(); // 200.0
	test02(); // 250.0 , 将 全局price 改成 250
	test01();// 250.0;如果修改double price = 250 后,会输出200
	getchar();
}

题目二:下面代码输出什么?

#include<stdio.h>
int n = 10; //全局变量
void func1(){
	int n = 20; //局部变量
	printf("func1 n: %d\n", n);
}

void func2(int n){ // 这里的n是形参n
	printf("func2 n: %d\n", n); 
}

void func3(){
	printf("func3 n: %d\n", n);  // 这个n就是全局的变量,因为自己里面没有定义也没有接收形参
}

int main(){
	int n = 30; //局部变量(main函数)
	func1();   // 输出20
	func2(n);  // 这里表示主函数中的n传到func2,输出30
	func3();  // 输出10
	//代码块由{}包围
	{
		int n = 40; //局部变量
		printf("block n: %d\n", n); // 这里的n就是代码块里面的n,输40
	}
	printf("main n: %d\n", n); // 这里找主方法里面的n,输出30
	getchar();
	return 0;
}

P 104 static关键字修饰变量 2023/1/15

一、基本介绍

介绍:static关键字在c语言中比较常用,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。

二、局部变量使用static修饰

  1. 局部变量被 static 修饰后,我们称为静态局部变量。

  2. 对应静态局部变量在声明时未赋初值,编译器也会把它初始化为0。

#include<stdio.h>
void main(){	
	static int n ; //  n静态局部变量,默认初始化值为0
	printf("\n n=%d",n);  // 会输出0
	getchar();
  1. 静态局部变量存储于进程的静态存储区(前面布局图)(全局性质),只会被初始一次,即使函数返回,它的值也会保持不变 [案例+图解]。
  2. 普通变量每次都会初始化,n在栈区,调用一次就会产生新的n;而静态局部变量,放在静态存储区,全局性质空间。
void fn_static(void) {
	static int n = 10;  // 静态局部变量
	printf("\nstatic n=%d\n", n);
	n++;
	printf("\nn++=%d\n", n);
}
int main(void) {
	fn_static();  // 这里输出10,11
	fn_static(); //输出11,12;因为只会被初始化一次,依照上一次的值进行++;所以输出11,12
}

三、全局变量使用static修饰

  1. 普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量),静态全局变量仅对当前文件可见,其他文件不可访问其他文件可以定义与其同名的变量,两者互不影响。[案例]
// file01中定义普通全局变量
int num = 10; // 普通全局变量
static int num2 = 20;  // 静态全局变量,只能在本文件中使用,而不能在其他文件使用。

// file02中引入输出
#include<stdio.h>
	// 在一个文件中,使用另外一个文件的全局变量,使用exter引入即可
extern int num;
extern int num2; // 但是这里虽然引入了,但是num2是静态全局变量,依然是不可以使用的
// 		int num2;  但是再定义一个num2两者是不会冲突的
void main(){
	printf("num = %d",num);
    printf("num2 = %d",num2);
	getchar();
}
  1. 定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

P 105 静态函数讲解和使用 2023/1/16

一、函数使用static修饰

  1. 函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数
static void fun2(void) {//静态函数,它只能在本文件中使用
	printf("hello from fun2.\n");
}
  1. 非静态函数可以在另一个文件中通过extern 引用 【案例】
extern void fun1(void);
  1. 静态函数只能在声明它的文件中可见,其他文件不能引用该函数[案例]
// file03 文件中
#include <stdio.h>
void fun1(void) {//普通函数(非静态函数)
	printf("hello from fun1.\n");
}
static void fun2(void) {//静态函数,它只能在本文件中使用
	printf("hello from fun2.\n");
}

// file04 文件中
#include<stdio.h>

extern void fun1(void);
extern void fun2(void);  // 这里引入静态函数,但是下面调用是会报错的

void main(void){
	fun1();
	fun2();  // 这里会报错无法解析外部符号报错
	getchar();
}
  1. 不同的文件可以使用相同名字的静态函数,互不影响[案例]
void fun2(){  // 在上面file03文件中有fun2,但是是静态函数,可以使用同名函数
//.....
};

void fun1(){ // 但是非静态函数时不能同名的,这里则会报错
//.....
};

P 106 常用的字符串函数 2023/1/16

一、字符串中常用的系统函数

**说明:**字符串(即 字符数组)在我们程序开发中,使用的是非常多的,常用的函数需要同学们掌握[带看手册或者官方编程指南 头文件 <string.h>]:

  • 得到字符串的长度

size_t strlen(const char *str)

计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

#include<stdio.h>
#include<string.h>  // 头文件中声明了字符串相关的系统函数

void main(){

	char src[50] = "abc", dest[50];   // 定义了两个字符数组(字符串),大小为50
	char * str = "abcdff";
	printf("str.len=%d", strlen(str));  // 统计字符串的大小,会返回6。
  • 拷贝字符串

char *strcpy(char *dest, const char *src)

src 所指向的字符串复制到 dest

	// 表示将hello拷贝到src
	// 注意:拷贝字符串会将原来的内容覆盖,所以会输出hello
	strcpy(src, "hello ");
	printf("\n src = %s",src);
	strcpy(dest, "尚硅谷");  // 同样是拷贝	
  • 连接字符串

char *strcat(char *dest, const char *src)

src 所指向的字符串追加到 dest 所指向的字符串的结尾。

	// strcat 是将src字符串内容连接或者追加到dest,但不会覆盖原有的内容,而是连接!!
	strcat(dest, src);  // 输出 hello尚硅谷
	printf("最终的目标字符串: |%s|", dest);	
	getchar();
}

P 107 常用的日期时间函数 2023/1/16

一、日期和时间相关函数

说明:在编程中,程序员会经常使用到日期相关的函数,比如:统计某段代码执行花费的时间等等。头文件是 <time.h>(学完结构体在温习)

  • 获取当前时间

char *ctime(const time_t *timer)

返回一个表示当地时间的字符串,当地时间是基于参数timer。

#include <stdio.h>
#include <time.h> // 该头文件中,声明了日期和时间相关的函数
int main () {
	time_t curtime;  // time_h是一个结构体类型
	time(&curtime);	// time() 完成初始化
	printf("当前时间 = %s", ctime(&curtime)); 
	// ctime 可以接受time_t这个类型,并且返回一个当前时间
	// 返回一个表示当地时间的字符串,当地时间是基于参数timer。
	getchar();
	return(0);
}
  • 编写一段代码来统计 函数test 执行的时间

double difftime(time_t time1, time_t time2)

返回 time1 和 time2 之间相差的秒数 (time1-time2)。

#include <stdio.h>
#include <time.h>
void test() {   // 运行test函数,看看执行花费的时间(一般用于做一些函数的时间测试)
	int i = 0;
	int sum = 0;
	int j = 0;
	for(i = 0; i < 77777777;i++) {
		sum = 0;
		for (j = 0; j< 10;j++) {
			sum += j;
		}
	}
}

int main(){

	// 先得到执行test前的时间

	time_t start_t,end_t;
	double diff_t;  // 存放时间差
	printf("程序启动…\n");
	time(&start_t); // 初始化得到当前时间

	test(); // 执行test

	// 再得到执行test后的时间
	time(&end_t);  // 得到当前时间
	diff_t = difftime(end_t,start_t);  // 时间差就是按秒计算,ent_t - start_t

	// 然后得到两个时间差就是耗用的时间
	printf("执行test()函数 耗用了%.2f 秒",diff_t);

	getchar();
return 0;
}

P 108 常用的数学函数 2023/1/16

一、数学相关函数

说明:math.h 头文件定义了各种数学函数和一个宏。在这个库中所有可用的功能都带有一个 double 类型的参数,且都返回 double 类型的结果

  • double exp(double x)返回 e 的 x 次幂的值。

  • double log(double x)返回 x 的自然对数(基数为 e 的对数)

  • double pow(double x, double y)返回 x 的 y 次幂。

  • double sqrt(double x)返回 x 的平方根。

  • double fabs(double x)返回 x 的绝对值。

#include <stdio.h>
#include <math.h>
void main (){
	double d1 = pow(2.0,3.0);  // 2的3次幂 输出8
	double d2 = sqrt(4.0);	// 4的开方为2
	double d3 = log(4.5);  // 以e为底的自然对数为1.504
	double d4 = fabs(-3.0); // -3的绝对值
	double d5 = exp(3.0);  // 以常数e为底数,3.0为指数的值为20.09
	printf("d1=%.2f\n", d1);
	printf("d2=%.2f\n", d2);
	printf("d3=%.2f\n", d3);
	printf("d4=%.2f\n", d4);
	printf("d5=%.2f\n", d5);
	getchar();
}

P 109 基本数据类型和字符串互转 2023/1/16

一、基本介绍

介绍:在程序开发中,我们经常需要将基本数据类型转成字符串类型(即 char数组 )。或者将字符串类型转成基本数据类型。

二、sprintf函数的用法

  1. sprintf和平时我们常用的printf函数的功能很相似。sprintf函数打印到字符串中,而printf函数打印输出到屏幕上。sprintf函数在我们完成其他数据类型转换成字符串类型的操作中应用广泛。

  2. 该函数包含在stdio.h的头文件中

  • 基本类型转字符串类型
#include<stdio.h>

void main() {
	char str1[20]; //字符数组,即字符串
	char str2[20];
	char str3[20];
	int a=20984,b=48090;
	double d=14.309948;

	//说明:
	// 1.sprintf是一个系统函数,可以将结果存放在字符串中
	// 2. 格式化的结果,会存放到str1中
	sprintf(str1,"%d %d",a,b);    // 存放到str1中
	sprintf(str2, "%.2f", d); 
	sprintf(str3, "%8.2f", d);   // %8.2f,格式化后总共有8位,小数点后占用2位,不够用空格占位
	printf("str1=%s str2=%s str3=%s", str1, str2, str3);
	getchar(); 
}
  • 字符串类型转基本数据类型

语法:通过<stdlib.h>的函数调用 atoi atof 即可

#include<stdio.h>
#include<stdlib.h>

void main() {
	char str[10] = "123456";
	char str2[10] = "12.67423";
	char str3[2] = "a";
	char str4[4] = "111";

	// 说明:
	// 1. atoi(str) 将str转成整数
	int num1 = atoi(str);
	short s1 = atoi(str4);

	// 说明:
	// 1.atoi(str2);将str2转成小数
	double d = atof(str2);
	
	// 说明:
	// 1. str3[0] 表示获取到str3这个字符串(数组)的第一个元素 'a'  
	char c = str3[0];
	printf("num1=%d d=%f c=%c s1=%d", num1, d, c, s1);
	getchar();
}

三、注意事项

  1. 在将char 数组 类型转成 基本数据类型时,要确保能够转成有效的数据,比如 我们可以把 “123” , 转成一个整数,但是不能把 “hello” 转成一个整数。
  1. 如果格式不正确,会默认转成 0 或者 0.0 [案例演示]
		char str[10] = "hello";
		int num1 = atoi(str);  // 强制转换会变成0

P 110 函数课堂练习题 2023/1/18

一、题目练习

题目一:函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔。 【课后练习】 => 将原来的代码封装到函数中即可

#include<stdio.h>
//  题目:函数可以没有返回值案例,编写一个函数,从终端输入一个整数(层数)打印出对应的金子塔。
// 层数 toltalLevel是通过形参传入
void Pyramid(int totalLevel){
	int a,b,c;   // 定义a表示层数,b表示* 的个数,c来表示空格
	for(a = 1;a <= totalLevel;a++){
		// 输出空格
		for(c=1; c<=totalLevel - a;c++){
			printf(" ");
		}
		for(b = 1;b<= 2 * a - 1;b++){   // 打印半个金字塔将b<=a即可,a为行数,每层的*与层数相等。
			//  打印整个金字塔就将改为b<=2*a-1,每一层的*的个数的规律		
			if(b == 1 || b == 2*a-1|| a == totalLevel){  // a = 5,表示最后一层,a表示层,所以使用最后一层
				printf("*");   // 打印空心金字塔,在此处考虑输出空格
			}else{
				printf(" ");
			}
		}
		printf("\n");
	}
	getchar();
}

void main(){
	int a = 5;
	printf("请输入金字塔的层数");
	scanf("%d",&a);
	// 调用函数
	Pyramid(a);
	getchar();
}

题目二:编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表。

#include<stdio.h>
void MultiplicationTable(int Num){
	int i,j;
	for(i = 1; i <=Num; i++){
		for(j = 1;j<=i; j++){
			printf("%d * %d = %d  ",j,i,i*j);
		}
		printf("\n");
	}
	getchar();
}

void main(){
	int Num = 10;   // 也可以改成手动输入
	MultiplicationTable(Num);
}

题目三:编写函数,对给定的一个二维数组(3×3)转置,这个题讲数组的时候再完成

题目四:定义函数,实现求两个double数字的最大值,并返回。

#include<stdio.h>
double getMax(double d1,double d2 ){
	return d1 > d2 ?d1:d2; // 使用一个三元运算
}
void main(){
	printf("最大值=%.2f",getMax(1.1,7.8));
	getchar();
}

题目五: 定义函数,求出三个int 类型数的和,并返回。

#include<stdio.h>
double Sum(int a,int b,int c){
	int res = 0;
	res = a + b + c;
	return  res;
//	return a+b+c;  建议直接返回
}
void main(){
	printf("和=%.2f",Sum(1,2,2));
	getchar();
}

题目六:定义函数,求出一个int类型数组中的最大值的索引 [数组学习后完成]

题目七:定义函数,可以接受一个double类型数组,使用冒泡法对其排序 [数组学习后完成]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一颗星星辰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值