C安全编程教学-声明和初始化-在使用前声明标识符(二)_算法

注:本课程参考文献《C安全编码标准》

 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~

目录

一.隐含函数声明

二.不安全代码

三.兼容修改方案

四.练习和答案


一.隐含函数声明

    隐含函数声明不允许;每个函数必须在调用之前明确声明,在C90标准中,如果函数在没有明确原型的情况下调用,编译器会提供一个隐含的声明。

    如果函数调用中带括号的参数列表之前的表达式只包含一个标识符,且这一标识符没有可见的声明,该标识符将隐含声明,就像函数调用最内部的代码块中出现extern int identifier();声明一样。

    此声明指出,该函数能够接受任意数量和类型的参数,并返回一个整型(int)值。但是,为了符合当前的C语言标准,程序员在调用任何函数之前,都必须显式声明该函数的原型。遵循C语言标准的编译器可能会支持隐式函数声明,也可能不支持,但C语言标准规定,在遇到未声明就使用的函数时,兼容的实现应当发出诊断信息。

二.不安全代码

    在探讨这个不兼容的代码示例时,若malloc()函数没有被明确声明(无论是直接声明还是通过包含stdlib.h头文件间接声明),仅遵循C90标准的编译器可能会默认将malloc()隐式声明为返回int类型的函数,即int malloc()。如果目标平台的int类型大小为32位,而指针类型的大小为64位,那么由malloc()隐式声明所返回的32位整数值可能会导致指针被截断,因为实际上需要一个64位的整数值来正确表示指针。

#include <stddef.h>
/* #include <stdlib.h> is missing */
int main(void){
    for (size_t i = 0;i < 100; ++i){
        /* int malloc() assumed */
        char *ptr = (char *)malloc(0x10000000);
        *ptr = 'a';
    }
    return 0;
}

三.兼容修改方案

    下面的相容解决方案在声明malloc()时包含了相应的头文件。

#include <stdlib.h>
int main(void){
    for (size_t i = 0;i < 100; ++i){
        char *ptr = (char *)malloc(0x10000000);
        *ptr = 'a';
    }
    return 0;
}

四.练习和答案

案例

    某开发者在编写一个程序时,尝试调用printf函数来输出一些信息,但他忘记包含stdio.h头文件。按照C90标准,编译器可能会对printf函数提供一个隐含的声明,假设它返回一个整型值并接受任意数量和类型的参数。然而,在现代C语言编程实践中,这种做法是不被推荐的,因为它可能导致未定义行为,特别是当编译器的默认行为与实际的函数声明不一致时。

代码示例

#include <stddef.h>  
/* #include <stdio.h> is missing */  
  
int main(void) {  
    for (size_t i = 0; i < 5; ++i) {  
        printf("Iteration %zu\n", i);  
    }  
    return 0;  
}

问题

  1. 在上述代码中,如果stdio.h头文件没有被包含,编译器可能会如何处理printf函数的调用?
  2. 如果编译器的默认行为是将printf隐式声明为返回int类型的函数,这可能导致什么问题?

答案

  1. 如果stdio.h头文件没有被包含,按照C90标准,编译器可能会对printf函数提供一个隐含的声明,即假设它返回一个整型值并接受任意数量和类型的参数。
  2. 如果编译器的默认行为是将printf隐式声明为返回int类型的函数,而实际上printf函数的正确声明是返回一个int类型的值,但其参数是一个格式字符串和可变数量的参数,那么这种隐式声明可能会导致未定义行为。特别是在一些平台上,如果编译器对函数参数的传递方式做了特定的假设(比如通过寄存器或特定的内存布局),那么隐式声明的函数可能无法正确地接收或处理这些参数,从而导致程序崩溃或产生不正确的输出。

兼容修改方案

    为了解决这个问题,开发者应该在源文件中包含stdio.h头文件,以确保printf函数被正确地声明。

#include <stdio.h>  
#include <stddef.h>  
  
int main(void) {  
    for (size_t i = 0; i < 5; ++i) {  
        printf("Iteration %zu\n", i);  
    }  
    return 0;  
}

    这样,编译器就可以根据stdio.h头文件中提供的printf函数的正确声明来处理函数调用,从而避免未定义行为的发生。

 非常感谢您花时间阅读我的博客,希望这些分享能为您带来启发和帮助。期待您的反馈与交流,让我们共同成长,再次感谢!

👇个人网站👇

安城安的云世界

 

C安全编程教学-声明和初始化-在使用前声明标识符(二)_算法_02