提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
C语言最主要的的就是函数,数组,指针,其中指针是C语言最核心的东西,对于新手也是最不友好的东西,我第一次学习,也觉得指针比较难,能避免使用,就几乎不用。
一、函数
C语言中函数的分类:
1. 库函数
库函数就是已经编译好的函数,我们只需要调用就可以,但是库函数在使用时,必须包含#include对应的头文件。
2. 自定义函数
自定义函数就是自己编写的函数,因为在程序中会频繁调用一些代码段,为了减少代码段的复写,也为了提高代码段的利用率,就会把这些频繁使用的函数编写成一个函数,放在主程序外部,方便每一次调用。但是切记一定要先声明再使用。
3.实参与形参
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
注意:对于形参的改变,并不会影响实参,下面给一段最简单的代码的例子:
#include<stdio.h>
//void Swap(int *a, int *b)
//{
// int tmp = *a;
// *a = *b;
// *b = tmp;
//}
void Swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
void main()
{
//实际参数
int a = 100;
int b = 200;
printf("Swap before a = %d, b = %d\n", a, b);
Swap(&a, &b);
printf("Swap After a = %d, b = %d\n", a, b);
}
调用函数之后,两者的值并没有发生改变,注释掉的那段函数,可以实现这个作用,它是使用改变地址,来实现两个数之间的转换。
4.嵌套调用和递归函数
嵌套调用就是函数和函数之间可以进行嵌套的调用,但是注意,如果一个函数还没有声明,就在另一个函数的内部调用的话,是不行的,所以要写那些函数,提前把这些函数统一声明一下,这是一个好的习惯,必须养成。
递归函数程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
递归的两个必要条件
存在限制条件,当满足这个限制条件的时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件。
例题:计算斐波那契数 1 1 2 3 5 8
#include<stdio.h>
size_t Fib(int n) //递归方式
{
if (n <= 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
size_t Fib1(int n) //数组赋值方式
{
if (n <= 2)
return 1;
int fib1 = 1;
int fib2 = 1;
int fib;
//迭代 杜绝栈的溢出
for (int i = 3; i <= n; ++i)
{
fib = fib1 + fib2;
fib1 = fib2;
fib2 = fib;
}
return fib;
}
void main(){
int n;
scanf("%d", &n);
size_t result = Fib(n);
printf("result = %u\n", result);
size_t result1 = Fib1(n);
printf("result = %u\n", result1);
}
这里用到了两种方式,递归与非递归(不考虑溢出问题)
例题2:编写一个函数 reverse_string(char * string)(递归实现)
字符串逆序
#include<stdio.h>
#include<string.h>
void reverse(char *string)//数组作为函数参数的时候,会退化为指针。
{
int len = strlen(string);//求出字符串的长度
if (*string) //不为空
{
char temp = *string;
*string = *(string + len - 1); //将最后一个字符给第一个,第一个保存在temp中,后面再赋值给最后一个
*(string + len - 1) = '\0';
reverse(string + 1); //递归的方式
*(string + len - 1) = temp;
}else
return;
}
int main()
{
char s[10] = "abcde";
reverse(s);
printf("%s\n", s);
return 0;
}
二、数组
数组是一组相同类型元素的集合。
例题:实现一个对整形数组的冒泡排序
#include<stdio.h>
void main(){
int arr[] = {
0, 1, 2, 6, 4, 8, 12, 3 };
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i<n - 1; ++i) //用于控制比较的趟数
{
for (int j = 0; j<n - i - 1; ++j) //用于控制每一趟的排序
{
if (arr[j] > arr[j + 1])
{
//交换两个数
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (int k = 0; k < n; k++){
printf("%d\t", arr[k]);
}
}
三、指针
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。
指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
1.指针和指针类型
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的
指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
2.指针运算
只能指针-指针
例:可以通过指针相减来求字符串的长度
#include<stdio.h>
//指针相减
void main()
{
char ar[5] = {
'a', 'b', 'c', 'd', 'e' };
char *end = &ar[5];
char *start = &ar[0];
int len = end - start;
printf("len = %d\n", len);
}
3.指针数组
指针数组就是存放指针的数组
总结
除了函数,数组,指针,还有一个操作符也比较重要,在这些里面都会嵌套使用,不需要刻意记,多写一些代码,就会熟悉,最后给出一个采用二进制输出一个数字的奇数位和偶数位的例题;
//获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
#include<stdio.h>
void main(){
int n;
scanf("%d", &n);
int ar[32] = {
0 };//定义一个数组,来存放给出数字的全部二进制位
for (int i = 0; i < 32; i+=2){
if (((n >> i) & 1) == 1)
ar[31 - i] = 1;
}
for (int j = 1; j < 32; j+=2){
if (((n >> j) & 1) == 1)
ar[31 - j] = 1;
}
printf("偶数位为:");
for (int i = 0; i <31; i += 2){
printf("%d", ar[i]);
}
printf("\n");
printf("奇数位为:");
for (int i = 1; i <32; i += 2){
printf("%d", ar[i]);
}
printf("\n");
}