C/C++教程

C语言 | 【04】基础(函数及指针)

本文主要是介绍C语言 | 【04】基础(函数及指针),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

    • 函数
        • 概述
          • 函数分类
          • 函数的作用
        • 函数的定义
          • 函数名
          • 形参列表
        • 函数的声明
        • 多文件
        • 头文件
    • 指针
        • 定义和使用
        • 指针类型
        • const修饰指针
        • 指针和数组
        • 指针和字符串
    • END

函数

概述

函数分类

C程序是由函数组成的,我们写的代码都是由主函数main(开始执行的。函数是C程序的基本模块,是用于完成特定任务的程序代码单元。

从函数定义的角度看,函数可分为系统函数和用户定义函数两种:

  • 系统函数,即库函数:这是由编译系统提供的,用户不必自己定义这些函数,可以直接使用它们,如我们常用的打印函数printf()。
  • 用户定义函数:用以解决用户的专门需要。
函数的作用
  • 函数可以让程序更加模块化,从而有利于程序的阅读修改完善

假如我们编写一个实现以下功能的程序:
  读入一行数字;对数字进行排序;找到它们的平均值;打印出一个柱状图。如果我们把这些操作直接写在main()里,这样可能会给用户感觉代码会有点凌乱。假如我们使用函数,这样可以让程序更加清晰模块化

函数的定义

  • 形参:变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。
  • 实参:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值。
#include<stdio.h>

// 函数定义
返回值类型 函数名称(参数列表){
	函数体
	return 返回值
}

// 【形参】:add()中的a,b
int add(int a, int b){
	return a+b;
}

// 【实参】:add()中的1,2
int main(){
	int t = add(1, 2);
	printf("%d", t);
	return 0;
}
函数名

理论上是可以随意起名字,最好起的名字见名知意。应该让用户看到这个函数名字就知道这个函数的功能
【注意】:函数名的后面有个圆换号(),代表这个为函数,不是普通的变量名。

形参列表

在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,因此称它们是形式参数或虚拟参数,简称形参,表示它们并不是实际存在的数据。所以,形参里的变量不能赋值。

形参与实参

  • 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用
  • 实参出现在主调函数中,进入被调函数后,实参也不能使用
  • 实参变量对形参变量的数据传递是“值传递”,即单向传递只由实参传给形参,而不能由形参传回来给实参
  • 在调用函数时,编译系统临时给形参分配存储单元。调用结束后,形参单元被释放。
  • 实参单元与形参单元是不同的单元。调用结束后,形参单元被释放,函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中实参的值。

函数的声明

  若函数写再main()函数后面,则需要声明。
声明extern 函数类型 函数名(参数列表)

多文件

在同一个文件夹中函数都是全局变量

头文件

作用

  • 声明函数、变量。
  • 系统库的调用。

使用

在头文件对应xx.c中实现xx.h声明的函数。

#include "xxx.h"

int main(){
	return 0;
}

防止头文件重复包含

为了避免同一个文件被多次include,有俩种方式:

  • 可在xx.h开头添加#pragma once
  • 或者添加ifndef __文件名_H__

指针

定义和使用

定义

数据类型 *变量名 是一种数据类型,用来指向一个变量的地址

使用

  • p = &变量名 - 接收该变量的地址
  • *p = 数值 - 给这个指针赋值。
  • 取地址 p 取值 *p

在这里插入图片描述

#include<stdio.h>

int main()
{
	int a = 10;
	int *p = &a;	// 将a的地址复制给p
	*p = 110;	// 给p赋值相等于给a赋值
	
	return 0;
}

指针在内存中的大小

通过 sizeof 对指针内存进行判断可知

在32位的系统中,所有的指针大小都是4个字节
64位系统, 8字节

#include<stdio.h>

int main()
{
	int a = 10;
	int *p = &a;	// 将a的地址复制给p
	
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(char *));
	return 0;
}

指针类型

野指针

指向一个未知的内存空间,可能在读写的时候出现错误。
系统规定:0-255之间的地址是系统保留的,阔以读,但不能写

空指针

指向内存编号0的空间, 操作该内存空间会报错,通常作为条件判断使用

万能指针

void 类型,如果赋值的话,需要强转类型

#include<stdio.h>
int main()
{
	int a = 10;
	void *p = &a;
	* (int *)p = 110;
	printf("%d", * (int *)p);
	
	return 0;
}

const修饰指针

  • const修饰常量:可以使用指针去修改他的值。
  • const int a = 100;
  • const 修饰 数据类型 * 不能改变指针变量指向的内存地址的值,可改变地址。
  • const int *p;
  • const 修饰 指针变量 能改变值,不能改变地址。
  • int * const p;
  • const 修饰 数据类型指针变量 都不能改变
  • const int * const p;

指针和数组

数组取值操作

当一个数组赋值给指针时指针有俩种取值方式:

  • 使用下标进行取值例如:p[1];
  • 使用 *(p+i); 通过循环可对数组逐一取值。

地址移动

当对p进行操作如:p++; p = p+1; 此时的p不在指向原来的地址,若原本赋值一个数组,则此时p指向的时首地址的下一个地址。

小案例:指针冒泡排序

#include<stdio.h>

// 冒泡排序
void get(int *p, int len)
{
	int i, j;
	for (i = 0; i<len -1; i++)
	{
		for (j=0; j<len-1-i; j++)
		{	
			int temp;
			if (p[j] < p[j+1])	// 判断后一位是否大于前一位
			{
				temp = p[j];
				p[j] = p[j+1];
				p[j+1] = temp;
			}
		}
	}
	
}

int main()
{
	int arr[] = {7,0,9,2,3,1,5,6,4,8};
	int len = sizeof(arr) / sizeof(arr[0]);	// 获取数组长度
	
	int *p = arr;
	get(p, len);	// 调用冒泡排序函数
	for (int i=0; i<10; i++)
	{
		printf("%d", arr[i]);	// 打印结果
	}

	return 0;
}

指针和字符串

相当于指针对数组进行操作

  • 字符串常量是一个常量数组,可以读取,但不能修改。
#include<stdio.h>

int main()
{
	char *src = "hello";	// 位于常量区不能修改
	char srcs[] = "hellow";		// 位于栈区可对字符串进行修改
}

END

以上是对函数以及指针的总结,后面对于字符串的内容相对较少,但与前面的内容没啥差别。就不过多的进行介绍。学完这部分内容,可以尝试着写出c语言的字符串的内置函数,例如读取长度字符串对比等,通过练习来提高对该内容的掌握。⛽

这篇关于C语言 | 【04】基础(函数及指针)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!