【问题标题】:Bounds check string pointer for return value in C function [duplicate]C函数中返回值的边界检查字符串指针[重复]
【发布时间】:2015-10-30 11:48:43
【问题描述】:

这是其中之一,“我知道有更好的方法”,问题。

下面的代码来自我正在做的一个更大的项目,将十六进制值转换为二进制字符串的辅助函数深埋在代码中。

本质上,我想确保*return_string指向的char array有足够的空间存放函数生成的二进制字符串。

就目前而言,如果我没有分配足够的内存,我会得到一个 SIGABRT。如果我只返回-1,那就更优雅了。

我所做的研究告诉我,我正在尝试做的事情有点棘手,因为实际上没有办法通过指针对空数组进行边界检查。

由于我不明白的原因,如果我使用通用宏:

#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))

在*return_string 上,我总是得到相同的值。我是 C 新手 - 我确定我在这里遗漏了一些基本的东西。

无论如何,这是代码。大家有什么推荐的?谢谢!

(仅供参考 - 我知道正如所写,我没有分配足够的内存)

(仅供参考 - 我也知道这不是十六进制到二进制转换的最快实现。O(n) 对于这个特定的应用程序来说已经足够好了)

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

int hex_string_to_bin_string(char *return_string, size_t bin_length, const char *input);

int main(int argc, const char * argv[]) {

    char return_string1[12];
    char return_string2[12];
    char return_string3[12];

    char input1[8] = "0x4708";
    char input2[8] = "4708";
    char bad_input[8] = "47Q8";

    fprintf(stderr, "answer should be:\t\t\t 0100011100001000\n");

    //test string 1
    if(hex_string_to_bin_string(return_string1, 16, input1) < 0 ) {

        fprintf(stderr, "FAILED return string 1\n");

    } else {

        fprintf(stderr, "return string 1:\t\t\t %s\n", return_string1);
    }

    //test string 2
    if(hex_string_to_bin_string(return_string2, 16, input2) < 0 ) {

        fprintf(stderr, "FAILED return string 2\n");

    } else {

        fprintf(stderr, "return string 2:\t\t\t %s\n", return_string2);
    }

    //test bad input
    if(hex_string_to_bin_string(return_string3, 16, bad_input) < 0) {
        fprintf(stderr, "PASSED bad character test\n");
    }

    else {

        fprintf(stderr, "FAILED bad character test\n");
    }

    return 0;
}

int hex_string_to_bin_string(char *return_string, size_t bin_length, const char *input) {

    /*
     0 - 0000
     1 - 0001
     2 - 0010
     3 - 0011
     4 - 0100
     5 - 0101
     6 - 0110
     7 - 0111
     8 - 1000
     9 - 1001
     A - 1010
     B - 1011
     C - 1100
     D - 1101
     E - 1110
     F - 1111
    */

    //check input to make sure length is good
    if(strlen(input) == 0 ) {

        return -1;
    }

    //check for leading '0x' and strip if necessary
    if(input[0] == '0' && input[1] == 'x') {
        input += 2;
    }

    //loop through input and replace
    int i;

    char bits[5];

    for(i=0; i<strlen(input); i++) {

        switch(input[i]) {

            case '0': {

                if( snprintf(bits, sizeof(bits), "0000") < 0) {

                    return -1;
                }

                break;
            }
            case '1': {

                if( snprintf(bits, sizeof(bits), "0001") < 0) {

                    return -1;
                }

                break;
            }
            case '2': {

                if( snprintf(bits, sizeof(bits), "0010") < 0) {

                    return -1;
                }

                break;
            }
            case '3': {

                if( snprintf(bits, sizeof(bits), "0011") < 0) {

                    return -1;
                }

                break;
            }
            case '4': {

                if( snprintf(bits, sizeof(bits), "0100") < 0) {

                    return -1;
                }

                break;
            }
            case '5': {

                if( snprintf(bits, sizeof(bits), "0101") < 0) {

                    return -1;
                }

                break;
            }
            case '6': {

                if( snprintf(bits, sizeof(bits), "0110") < 0) {

                    return -1;
                }

                break;
            }
            case '7': {

                if( snprintf(bits, sizeof(bits), "0111") < 0) {

                    return -1;
                }

                break;
            }
            case '8': {

                if( snprintf(bits, sizeof(bits), "1000") < 0) {

                    return -1;
                }

                break;
            }
            case '9': {

                if( snprintf(bits, sizeof(bits), "1001") < 0) {

                    return -1;
                }

                break;
            }
            case 'A': {

                if( snprintf(bits, sizeof(bits), "1010") < 0) {

                    return -1;
                }

                break;
            }
            case 'B': {

                if( snprintf(bits, sizeof(bits), "1011") < 0) {

                    return -1;
                }

                break;
            }
            case 'C': {

                if( snprintf(bits, sizeof(bits), "1100") < 0) {

                    return -1;
                }

                break;
            }
            case 'D': {

                if( snprintf(bits, sizeof(bits), "1101") < 0) {

                    return -1;
                }

                break;
            }
            case 'E': {

                if( snprintf(bits, sizeof(bits), "1110") < 0) {

                    return -1;
                }

                break;
            }
            case 'F': {

                if( snprintf(bits, sizeof(bits), "1111") < 0) {

                    return -1;
                }

                break;
            }

            default: {

                return -1;
            }
        }

        if(i == 0) {
            snprintf(return_string, bin_length+1, "%s", bits);
        }

        else {
            snprintf(return_string, bin_length+1, "%s%s", return_string, bits);
        }
    }

    return 0;
}

【问题讨论】:

  • sizeof return_string 告诉你return_string 的大小,它是一个指针。不是它可能指向的大小。
  • 你不能 snprintf 一个字符串到它自己上;那是undefined behaviour。
  • 感谢@alk 和@M.M!两者都有丰富的知识。

标签: c


【解决方案1】:

您必须将输出缓冲区的大小作为参数传递。

由于参数的类型是char *,因此无法神奇地计算出可存储在那里的字符数。该信息根本不存在,这就是为什么您作为程序员必须负责将其传递给函数。

【讨论】:

    【解决方案2】:

    我建议将转换函数的接口更改为包含接收文本的缓冲区的长度。

    这就是 C 标准库的各种字符串相关函数的安全版本的方法,例如 sprintf_s()。

    在 C 中,文本字符串实际上是一个字符数组。文本字符串以二进制零结尾以指示字符串的结尾,但这不是编译器强制执行的,而不是常量字符串,例如char textStuff[] = "this is text"; 之类的定义,它将创建一个数组textStuff,该数组具有足够的数组元素包含十二个文本字符this is text 以及字符串结尾的终止二进制零的第十三个字符。

    Java 等语言使用资源句柄而不是指针,因此在运行时可以使用数组长度等附加信息。而其他语言有一个实际的文本字符串类型。

    在定义数组时,编译器知道数组的大小,因此要查找数组中的元素数,您可以执行sizeof(textStuff)/sizeof(textStuff[0]) 之类的操作,通过除以总数来确定元素数数组的大小乘以数组的第一个元素的大小。

    但是,一旦调用了函数,将接口中的数组指定为未知大小的数组或指针,该数组本质上就会转换为指针以及在源中数组所在位置可用的任何大小信息已定义的编译器不再可用。

    【讨论】:

      猜你喜欢
      • 2018-03-23
      • 2015-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-16
      • 1970-01-01
      • 2020-04-11
      • 1970-01-01
      相关资源
      最近更新 更多