const关键字、函数

const关键字、函数

一、const关键字

const修饰变量的时候,表示不能通过变量名,修改变量的值。

const int a = 10;
a = 20; //错误的  const 修饰的是一个只读变量 不能通过变量名修改

const 修饰指针的时候

const int *p;
int const *p;
int * const p;
const int * const p;

//区别的时候 注意 * 和const 的相对位置关系
//如果const在 *的左边表示 修饰的是 *p
    //不能通过 p 修改指向的空间的内容
    //但是 p 的指向是允许修改的
//如果const在 *的右边表示 修饰的是 p
    //指针的指向不能修改
    //但是允许通过指针修改指向的空间里的内容
//如果*的两边都有const,
    //表示指针的指向不能修改,
    //也不能通过指针修改指向空间里的内容

例:

#include <stdio.h>

int main(int argc, const char *argv[])
{
#if 0
	int a = 100;
	int b = 200;
	const int *p = &a;
	//*p = 1234;//错误的
	a = 1234; //通过变量自身是可以修改的
	p = &b;//正确的
#endif
	
#if 0
	//和上面的写法是等价的 只不过一般用上面的写法比较多
	int a = 100;
	int b = 200;
	int const *p = &a;
	//*p = 1234;//错误的
	p = &b;//正确的
#endif

#if 0
	int a = 100;
	int b = 200;
	int * const p = &a;
	*p = 1234;//正确的
	//p = &b; //错误的
#endif

	int a = 100;
	int b = 200;
	const int * const p = &a;
	//*p = 1234;//错误的
	//p = &b; //错误的

	return 0;
}

二、函数

2.1 概念

将实现某些功能的代码,封装成代码块,每次想使用这个功能的时候

无需编写重复的代码,只需要通过代码块的名字调用即可。

这个代码块就叫做函数,代码块的名字就叫做函数名。

如:printf strcpy strcat atoi 等 都是函数

2.2 函数的定义和调用

定义函数的格式:

返回值类型  函数名(函数的参数列表){
    函数体;//也就是我们要实现功能的代码块
}

函数名 是一个标识符,要符合标识符的命名规范。

例:

#include <stdio.h>

//int 是函数的返回值类型 如果有返回值就写类型 
//		如果没有返回值 可以写成 void 但是注意不能不写
//print_menu 是函数名 是我们自己起的名字
//()	里面是函数的参数列表 如果有参数就写 没有参数可以写void 或者直接空着不写
//{}	里面扩住的代码就是函数体 就是实现功能的代码块
//return 是用来返回函数运行的结果的 如果有返回值 就写
//			要注意返回值的类型要和函数名前面的类型保持一致
//			如果没有返回值  可以不写return  或者 直接写 return;
int print_menu(void){
	printf("--------------------------\n");
	printf("| 1.注册  2.登陆  3.退出 |\n");
	printf("--------------------------\n");
	printf("please input your choose : ");
	return 0;
}

//函数一旦定义好之后 就可以在其他函数中调用了

//函数中的代码如果没有被调用 是不会执行的
void my_test(){
	printf("my_test start\n");
	print_menu();
	printf("my_test end\n");
}

int main(int argc, const char *argv[])
{
	//通过函数名即可调用函数
	// () 里面是要给函数传的参数 如果没有参数 可以不写 但是 () 必须写
	//程序执行到调用函数的位置  就会跳转到函数内部执行代码
	//			执行完在跳转会函数调用处	继续向下执行
	print_menu();
	int choose = 0;
	scanf("%d", &choose);
	printf("choose = %d\n", choose);

	my_test();

	return 0;
}

2.3 函数的声明

函数定义好之后,可以在其他函数中掉用,如果我们把所有函数都定义在main函数的上面

那么在main函数中调用是没有问题的,但是函数之间相互调用,就有可能出现不认识的情况

这时就需要用到函数的声明了。

#include <stdio.h>

//函数的声明的格式
void func1();
void func2();
void func3();

int main(int argc, const char *argv[])
{
	func1();	
	return 0;
}

//函数的定义
void func1(){
	printf("i am func1..\n");
	func2();
}

//函数的定义
void func2(){
	printf("i am func2..\n");
	func3();
}

//函数的定义
void func3(){
	printf("i am func3..\n");
}

2.4 函数的参数

函数为什么要有参数?

在函数实现功能的过程中,有些值,函数里面没有,就需要调用者

在调用函数的时候,通过参数给函数传递过来。

例:

#include <stdio.h>

//有参数的函数声明
//void my_add(int x, int y);
void my_add(int, int);//也可以只写形参的类型 不写形参的名字

int main(int argc, const char *argv[])
{
	int a = 100;
	int b = 200;
	//有参数的函数调用
	//调用函数时传递的参数叫做实际参数 简称--实参
     //调用函数时 实参的类型和个数 必须和形参保持一致
	my_add(a, b);

	my_add(10, 20);

	return 0;
}

//定义函数时,函数的()里面的叫做函数的形式参数 简称--形参
//形参相当于对函数参数格式的约定 约定了调用者需要传 几个及什么类型的参数
//在调用函数的过程中 操作系统会给函数的形参分配空间 然后用实参来初始化形参
//注意 在函数内部 对形参如何修改都不会影响实参 因为 他们在不同的内存空间上
void my_add(int x, int y){
	int temp = x+y;
	printf("%d+%d = %d\n", x, y, x+y);
}
//形参占用的内存空间 在函数调用结束时 会被操作系统回收

2.5 函数的返回值

函数为什么要有返回值?

有些时候,函数运行的结果需要返回给调用处,供后面使用,

这时候就需要用到返回值了。需要返回值就写,不需要可以不写。

#include <stdio.h>

int my_add(int x, int y){
	int temp = x+y;
		//return 后面可以是变量 也可以是常量 也可以是表达式
		//只要保证和 函数名前面的类型一致即可
	return temp; //遇到return 就立即返回 后面的代码都不执行了
	printf("hello world\n");//不执行
}

int main(int argc, const char *argv[])
{
	int a = 10;
	int b = 20;
	int ret = 0;//定义一个变量 用来接收函数的返回值
	ret = my_add(a, b);
	printf("ret = %d\n", ret);

	//即使函数有返回值 也可以不接 具体业务逻辑
	my_add(100, 200);

	return 0;
}

实际开发的过程中,定义的函数一般都是有返回值的,

因为可以通过返回值来判断函数的执行情况,从而决定如何处理

如下面的伪代码:

#define ERR_NET -1
#define ERR_DATABASE -2
#define ERR_LOG -3
#define ERR_USER -4
#define SUCCESE 0

int pro_init(){
    if(连接网络失败){
        return ERR_NET;
    }
    if(连接数据库失败){
        return ERR_DATABASE;
    }
    if(加载日志文件失败){
        return ERR_LOG;
    }
    if(加载用户信息失败){
        return ERR_USER;
    }
    return SUCCESE;
}

int main(){
    int ret = pro_init();
    if(SUCCESE != ret){
        //可以根据 ret 的不同走不同的处理逻辑
    }
    
    return 0;
}

练习:

1.封装一个 [m,n] 求和的函数,m和n由参数传递

通过返回值返回求和的结果,调用并测试

#include <stdio.h>

int my_sum(int m, int n){
	int i = 0;
	int sum = 0;
	for(i = m; i <= n; i++){
		sum+=i;
	}
	return sum;
}

int main(int argc, const char *argv[])
{
	int ret = my_sum(1, 10);
	printf("ret = %d\n", ret);//55

	printf("%d\n", my_sum(1,100));//5050

	return 0;
}

2.编写4个函数 分别实现两个整数的加减乘除的功能,返回值返回计算的结果

my_add

my_sub

my_mul

my_div

#include <stdio.h>

int my_add(int x, int y){
	return x+y;
}

int my_sub(int x, int y){
	return x-y;
}

int my_mul(int x, int y){
	return x*y;
}

double my_div(int x, int y){
	return (double)x/(double)y;
}

int main(int argc, const char *argv[])
{
	printf("%d\n", my_add(7, 4));
	printf("%d\n", my_sub(7, 4));
	printf("%d\n", my_mul(7, 4));
	printf("%lf\n", my_div(7, 4));
	return 0;
}

2.6 全局和局部

#include <stdio.h>

//生命周期:占用的内存空间何时被回收
//作用域:在哪个范围内能访问

//全局变量:没有被任何{}包住的变量
int value2 = 1314;
//生命周期:整个程序结束
//作用域:整个文件

void my_test(){
	int value3 = 520;//局部
	//printf("value1 = %d\n", value1);//访问不了
	printf("value2 = %d\n", value2);//1314
	printf("value3 = %d\n", value3);//520
}

int main(int argc, const char *argv[])
{
	//局部变量:被{}包住的变量
	int value1 = 10;
	//生命周期:最近的{}结束
	//作用域:最近的{}
	
	printf("value1 = %d\n", value1);//10
	printf("value2 = %d\n", value2);//1314
	//printf("value3 = %d\n", value3);//访问不了
	
	my_test();

	return 0;
}

2.7 函数的传参方式

2.7.1 全局传参

----了解即可 基本不使用

#include <stdio.h>

int ret = 0;
int lvalue = 0;
int rvalue = 0;

void my_add(){
	ret = lvalue+rvalue;
}

int main(int argc, const char *argv[])
{
	lvalue = 10;
	rvalue = 20;
	my_add();
	printf("ret = %d\n", ret);//30

	return 0;
}

2.7.2 复制传参(值传递)

形参只是将实参的值保存了一份儿

在函数内部无论怎样修改形参,都不会影响实参,因为他们不在同一块内存空间上。

#include <stdio.h>

//功能:将参数放大10倍 再求和
int my_add_ten(int x, int y){
	printf("func : &x = %p, &y = %p\n", &x, &y);//和下面main函数中xy的地址不一样
	x = x * 10;
	y = y * 10;
	int temp = x+y;
	printf("func : x = %d, y = %d\n", x, y);//100 200
	return temp;
}

int main(int argc, const char *argv[])
{
	int x = 10;
	int y = 20;
	printf("main : &x = %p, &y = %p\n", &x, &y);//和上面子函数中xy的地址不一样
	printf("main 前 : x = %d, y = %d\n", x, y);//10 20
	int ret = my_add_ten(x, y);
	printf("ret = %d\n", ret);  //300
	printf("main 后 : x = %d, y = %d\n", x, y);//10 20

	return 0;
}

2.7.3 地址传参(地址传递)

如果想在函数中修改实参的值,就需要将实参的地址传给函数。

#include <stdio.h>

//此处 x 和 y 是值传递  z是地址传递
void my_add1(int x, int y, int *z){
	printf("my_add1 : z = %p\n", z);
	*z = x+y;
	printf("my_add1 : z = %d\n", *z);//30
}

int main(int argc, const char *argv[])
{
	int a = 10;
	int b = 20;
	int ret1 = 0;
	printf("main : &ret1 = %p\n", &ret1);
	my_add1(a, b, &ret1);
	printf("ret1 = %d\n", ret1); //30

	return 0;
}

练习:

定义一个函数,实现两个整数的交换。

#include <stdio.h>

void my_swap(int *p, int *q){
	int temp = *p;
	*p = *q;
	*q = temp;
}

int main(int argc, const char *argv[])
{
	int x = 10;
	int y = 20;
	my_swap(&x, &y);
	printf("x = %d  y = %d\n", x, y);//20 10
	return 0;
}

//形参是指针,也不一定是地址传递,有可能是指针的值传递。

#include <stdio.h>

int m = 10;
int n = 20;
#if 0
//功能:改变指针指向
void my_chage1(int *q){
	q = &n;
}
#endif

void my_chage2(int **q){
	*q = &n;
}

int main(int argc, const char *argv[])
{
#if 0
	int *p = &m;
	printf("*p = %d\n", *p);//10
	my_chage1(p);
	printf("*p = %d\n", *p);//10
#endif
	
	int *p = &m;
	printf("*p = %d\n", *p);//10
	my_chage2(&p);
	printf("*p = %d\n", *p);//20
	printf("p = %p  &n = %p\n", p, &n);//一样的

	return 0;
}

2.8 数组的传参方式

2.8.1 字符串的传参方式

只传首地址即可,因为字符串有 ‘\0’ 来作为结束的标志

#include <stdio.h>

char *my_strcpy(char *dest, const char *src){
	char *temp = dest;//备份目标字符串的首地址 用作返回值
	while(*src != '\0'){
		*dest++ = *src++;
	}
	*dest = *src;
	return temp;
}

int main(int argc, const char *argv[])
{
	char s1[32] = "hello world";
	char s2[32] = "beijing";

	char *p = my_strcpy(s1, s2);

	printf("s1 = %s\n", s1);
	printf("s2 = %s\n", s2);
	printf("p = %s\n", p);

	return 0;
}

2.8.2 整型数组的传参方式

既需要传首地址,也需要传数组的长度

因为 整型数组是没有结束标志的。

#include <stdio.h>

//功能 遍历一维数组
void print_array(int *p, int len){//常用的写法
	int i = 0;
	for(i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

//数组传参使用下面的写法也可以
//下面这两种写法叫做 代码的自注释 
//这种写法的本质 p 也是指针
void print_array2(int p[100], int len){
//void print_array2(int p[], int len){
	printf("sizeof(p) = %ld\n", sizeof(p)); // 8  说明p是指针
	int i = 0;
	for(i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

int main(int argc, const char *argv[])
{
	int s1[5] = {1,2,3,4,5};
	print_array(s1, 5);

	int s2[10] = {1,2,3,4,5,6,7,8,9,10};
	print_array(s2, 10);

	print_array2(s2, 10);

	return 0;
}

练习:

封装一个函数,功能是查找一维数组中的最大元素,

通过返回值返回最大元素。

定义一个长度为10的数组,调用测试。

#include <stdio.h>

int find_max(int *p, int len){
	int max_index = 0;
	int i = 0;
	for(i = 1; i < len; i++){
		if(p[max_index] < p[i]){
			max_index = i;
		}
	}
	return p[max_index];
}

int main(int argc, const char *argv[])
{
	int s[8] = {11,22,33,44,34,666,78,9};
	int ret = find_max(s, 8);
	printf("ret = %d\n", ret);

	return 0;
}

2.9 二维数组的传参方式

需要使用 数组指针 最为函数的形参。

例:

#include <stdio.h>

//遍历二维数组
void print_array(int (*p)[4], int hang, int lie){
	int i = 0;
	int j = 0;
	for(i = 0; i < hang; i++){
		for(j = 0; j < lie; j++){
			printf("%d  ", p[i][j]);
		}
		printf("\n");
	}
}

int main(int argc, const char *argv[])
{
	int s[3][4] = {{1,2,3,4},
					{5,6,7,8},
					{9,10,11,12}};
	print_array(s, 3, 4);

	return 0;
}

2.10 main函数的参数

在这里插入图片描述

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//argc 是执行程序时命令行参数的个数(包括可执行文件名的)
	printf("argc = %d\n", argc);

	int i = 0;
	for(i = 0; i < argc; i++){
		printf("argv[%d] = %s\n", i, argv[i]);
	}

	//argv[0] 就是指向可执行文件名的指针
	printf("argv[0] = %s\n", argv[0]);
	
	return 0;
}

思考:将 const char *argv[] 改成 const char **argv 可不可以?

答案:可以 因为操作空间都是 一个指针的大小

1.将前面讲的冒泡排序的逻辑封装成函数

要求,多设置一个变量 flag, 别人调用时

给 flag 传 0 升序

给 flag 传 1 降序

#include <stdio.h>

int my_swap(int *p, int x, int y){
	int temp = p[x];
	p[x] = p[y];
	p[y] = temp;
}

int my_sort(int *p, int len, int flag){ //flag 0 升序  1 降序
	if(NULL == p){//防止传参传的是空指针的  检查
		return -1;
	}
	int i = 0;
	int j = 0;
	if(0 == flag){//升序
		for(i = 0; i < len-1; i++){
			for(j = 0; j < len-i-1; j++){
				if(p[j] > p[j+1]){
					my_swap(p, j, j+1);
				}
			}
		}	
	}else if(1 == flag){//降序
		for(i = 0; i < len-1; i++){
			for(j = 0; j < len-i-1; j++){
				if(*(p+j) < *(p+j+1)){
					my_swap(p, j, j+1);
				}
			}
		}	
	}
	return 0;
}

void print_array(int *p, int len){
	int i = 0;
	for(i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

int main(int argc, const char *argv[])
{
	int s[10] = {11,22,33,66,44,55,88,77,99,10};
	print_array(s, 10);

	//升序
	my_sort(s, 10, 0);
	print_array(s, 10);

	//降序
	my_sort(s, 10, 1);
	print_array(s, 10);

	return 0;
}

2.实现一个简易的计算器功能(能实现加减就行),要求命令行传参

如: ./a.out 10 + 20 -->>30

./a.out 5 - 4 —>>1

#include <stdio.h>

int my_swap(int *p, int x, int y){
	int temp = p[x];
	p[x] = p[y];
	p[y] = temp;
}

int my_sort(int *p, int len, int flag){ //flag 0 升序  1 降序
	if(NULL == p){//防止传参传的是空指针的  检查
		return -1;
	}
	int i = 0;
	int j = 0;
	if(0 == flag){//升序
		for(i = 0; i < len-1; i++){
			for(j = 0; j < len-i-1; j++){
				if(p[j] > p[j+1]){
					my_swap(p, j, j+1);
				}
			}
		}	
	}else if(1 == flag){//降序
		for(i = 0; i < len-1; i++){
			for(j = 0; j < len-i-1; j++){
				if(*(p+j) < *(p+j+1)){
					my_swap(p, j, j+1);
				}
			}
		}	
	}
	return 0;
}

void print_array(int *p, int len){
	int i = 0;
	for(i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

int main(int argc, const char *argv[])
{
	int s[10] = {11,22,33,66,44,55,88,77,99,10};
	print_array(s, 10);

	//升序
	my_sort(s, 10, 0);
	print_array(s, 10);

	//降序
	my_sort(s, 10, 1);
	print_array(s, 10);

	return 0;
}

2.实现一个简易的计算器功能(能实现加减就行),要求命令行传参

如: ./a.out 10 + 20 -->>30

./a.out 5 - 4 —>>1

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

int main(int argc, const char *argv[])
{
	//对命令行参数的个数 做检查
	if(4 != argc){
		printf("Usage : %s lvalue opretor rvalue\n", argv[0]);
		return -1;
	}

	int lvalue = atoi(argv[1]);
	int rvalue = atoi(argv[3]);
	char opretor = *argv[2];
	
	switch(opretor){
		case '+':
			printf("%d\n", lvalue + rvalue);
			break;
		case '-':
			printf("%d\n", lvalue - rvalue);
			break;
		default:
			printf("目前只支持加减运算..\n");
			break;
	}
	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值