C语言学习记录——팔 函数和递归(1)

目录

一、库函数

怎样使用文档来学习库函数

strcpy,strlen

memset

二、自定义函数

三、函数的参数

四、函数调用

五、练习

1、找出100-200之间所有的素数

2、写函数判断是否是闰年

3、写函数,实现一个整形有序数组的二分查找

4、写个函数,每调用一次这个函数,就会将num的值增加1

六、函数的嵌套调用和链式访问

一些练习

七、函数的声明和定义


一、库函数

IO函数

字符串操作函数 (比如strlen)

字符操作函数(比如把大写转小写)

内存操作函数(memset)

时间/日期函数(time)

数学函数(sqrt开平方)

其他库函数

怎样使用文档来学习库函数

strcpy,strlen

char * strcpy (char * destination, const char * source)把源头拷贝到目的地。

Copies the C string pointed by source into the array pointed by destination, including the terminating null charcter(and stopping at that point).

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.

#include <string.h>

int main()
{
    char arr1[] = "zyd";
    char arr2[20] = "*********";
    printf("%d\n", strlen(arr2));
    strcpy(arr2, arr1);
    printf("%s\n", arr2);
    printf("%d\n", strlen(arr2));
    //strcpy—字符串拷贝—string copy
    //strlen—string length—字符串长度有关
    return 0;
}

打印出来arr2就会是zyd了,后面有\0。源头如果比目的地长度还长,那么就会溢出,要保证目的地字符串串长度足够长。

memset

void * memset (void * ptr, int value, size_t num)

Fill block of memory

Sets the first num bytes of the block of memory pointed by ptr to  thr specified value(interpreted as an unsigned char)

    char arr[] = "hello world";
    memset(arr, '*', 6);
    printf("%s\n", arr);
    memset(arr, '*', 5);
    printf("%s\n", arr);

memset里第一个是要改的对象,第二个为*,就是要改成什么,第三个是数量。如果是5,那就把hello改成了星号,中间还会有个空格,结果是***** world。不过会发现一个问题,memset中第二个为int类型,星号并不是int类型。其实*在内存中存储时会存储ANCII值,所以并不会起冲突。memset是直接改变了原字符串,所以先5后6可以看到,第一个打印5个*一个空格+world,第二个则是把那个空格用*填上,先6后5就只能看到两个输出都一样,都没有空格了。

二、自定义函数

除了库函数,程序员自己也可以创建函数。标明函数类型,函数名,括号里的参数。

int get_max(int x, int y)
{
    if (x > y)
        return x;
    else
        return y;
}

int main()
{
    int a = 10;
    int b = 20;
    int max = get_max(a, b);
    printf("max = %d\n", max);
    return 0;
}

比较两个数的大小

另一个,交换两个整数的值

void Swap1(int x, int y)
{
    int tmp = 0;
    tmp = x;
    x = y;
    y = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("a = %d\n b = %d\n", a, b);
    Swap1(a, b);
    printf("a = %d\n b = %d\n", a, b);
    return 0;
}

void表示没有返回值。但是这样并没有交换。因为当监视整个过程时,会发现虽然xy已经被给了值,但是这个ab和xy的地址不一样,所以最后输出ab仍然是那个值

void Swap2(int* x, int* y)
{
    int tmp = 0;
    tmp = *x;
    *x = *y;
    *y = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("a = %d\n b = %d\n", a, b);
    Swap1(&a, &b);
    printf("a = %d\n b = %d\n", a, b);
    return 0;
}

三、函数的参数

实际参数(实参):真实传给函数的参数。实参可以是常量,变量,表达式,函数等。在进行调用时,它们都必须有确定的值,以便把这些值传送给形参。

形式参数(形参):形参是指函数名后括号中的变量,因为形参只有在函数被调用的过程中才实例化(分配内存单元),所以叫形参。形参当函数调用完成之后就自动销毁了。因此形参只在函数中有效。

形参比如,Swap程序中如果没有对a和b的使用,那么x和y也就没有使用空间,所以是形参,只有开始用这个函数时,才会实际存储到内存中。

当实参传给形参的时候,形参实例化后其实是实参的一种临时拷贝,对形参修改不会影响到实参。

四、函数调用

传值调用:函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参

Swap1就是一种传值调用,把ab的值传给xy。

传址调用:传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式;这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

Swap2是一种传址调用,把变量地址传过去。

区别:可以看到Swap1中,ab和xy地址不一样,只是值传过去,xy创立了独自的空间。而Swap2在这个函数内部可以操作函数外部的ab,需要将地址传过去。所以如果想改变函数外部的变量,要考虑传址。如果仅仅想要获得值,那么传值。

五、练习

1、找出100-200之间所有的素数

int is_prime(int n)
{
    int j = 0;
    for (j = 2; j <= sqrt(n); j++)
    {
        if (n % j == 0)
            return 0;
    }
    return 1;
}

int main()
{
    int i = 0;
    for (i = 100; i <= 200; i++)
    {
        if (is_prime(i) == 1)
            printf("%d ", i);
    }
    return 0;
}

2、写函数判断是否是闰年

int is_leap_year(int y)
{
    if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0))
        return 1;
    else
        return 0;
}

int main()
{
    int y = 0;
    for (y = 1000; y <= 2000; y++)
    {
        if (1 == is_leap_year(y))
            printf("%d ", y);
    }
    return 0;
}

3、写函数,实现一个整形有序数组的二分查找

int binary_search(int arr[], int k)
{
    int sz = sizeof(arr) / sizeof(arr[0]);
    int left = 0;
    int right = sz - 1;
    while (1)
    {
        int mid = (left + right) / 2;
        if (arr[mid] < k)
            left = mid + 1;
        else if (arr[mid] > k)
            right = mid - 1;
        else
            return mid;
    }
    return -1;
}

int main()
{
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10 };
    int k = 7;
    int sz = sizeof(arr) / sizeof(arr[0]);
    int ret = binary_search(arr, k);
    if (ret == -1)
        printf("找不到指定的数字\n");
    else
        printf("找到了,下标是: %d\n", ret);
    return 0;
}

但是这样就会出错,会打印出找不到指定的数字,如果监视binary函数,会发现,sz的值为1。因为这个程序把arr传过去,但是实际上传了第一个数字的地址,所以可以看出虽然写着int arr[],但是这是一个指针变量,所以也就出来了1。现在改一下:

int binary_search(int arr[], int k, int sz)
{
    int left = 0;
    int right = sz - 1;
    while (1)
    {
        int mid = (left + right) / 2;
        if (arr[mid] < k)
            left = mid + 1;
        else if (arr[mid] > k)
            right = mid - 1;
        else
            return mid;
    }
    return -1;
}

int main()
{
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10 };
    int k = 7;
    int sz = sizeof(arr) / sizeof(arr[0]);
    int ret = binary_search(arr, k, sz);
    if (ret == -1)
        printf("找不到指定的数字\n");
    else
        printf("找到了,下标是: %d\n", ret);
    return 0;
}

这样就对了

4、写个函数,每调用一次这个函数,就会将num的值增加1

void Add(int* p)
{
    (*p)++;
}

int main()
{
    int num = 0;
    Add(&num);
    printf("num = %d\n", num);
    Add(&num);
    printf("num = %d\n", num);
    Add(&num);
    printf("num = %d\n", num);
    return 0;
}

六、函数的嵌套调用和链式访问

嵌套调用:可以定义一个函数,在其他定义函数里也可以使用这个。

链式访问:把一个函数的返回值作为另一个函数的参数。

int main()
{
    int len = 0;
    len = strlen("abc");
    printf("%d\n", len);
    return 0;
}

这样结果是3。可以简化:

int main()
{
    printf("%d\n", strlen("abc"));
    return 0;
}

这也就是链式访问。

一些练习

int main()
{
    printf("%d\n", printf("%d", printf("%d", 43)));
    return 0;
}

结果是4321。

123个printf函数,需要用到返回值。查文档后,printf函数返回值类型是int类型,返回的是打印在屏幕上的字符个数。

七、函数的声明和定义

函数声明:函数在使用前都必须声明,告诉程序有这样一个函数。如同变量一样,int a = 1,这就是一种声明。

函数定义:刚才的自定义函数就是定义函数

比如:

int Add(int x, int y);

告诉程序有一个Add程序,两个int类型的参数。不过写的时候,在用一个源文件时,更多使用函数定义,如同之前自定义函数一样,定义完后就可以使用,那么不需要再单独声明。

另一种方法,我们可以在另一个文件放上定义函数,然后在一个文件里引用,#include "add.h"在同一个文件里写函数,可能会混乱,当工作量大时。

结束。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值