[英]using strtol on a string literal causing segmentation fault
我有一个getline()
获得的字符串(更确切地说,我使用循环和getline
逐行读取文件)
假设这条线是12|34|
然后我使用strtok()将其删除为substr = strtok(line, "|");
并将它们存储到带有循环的字符串数组中, part[index] = substr;
所以这里的[0]部分应该是“12”而部分[0]是“34”我想使用strtol,但是我已经检查过它不能用于字符串文字,然后我尝试使用代码。
char *temp = strdup(part[1]);
char **ptr;
long ret = strtol(temp, ptr, 10);
printf("%x\n", ret);
当我读到第二行时,会导致分段错误。 我怎样才能真正使用strtol
将字符串转换为整数
问题是ptr
没有初始化。 因此,当strtol
尝试在地址ptr
处写入时,它会崩溃(或未定义的行为)。
您必须传递指针的有效地址以存储最后一个未处理的char,如:
char *ptr;
long ret = strtol(temp, &ptr, 10);
&ptr
有效并指向自动变量存储位置到ptr
你是在滥用strtol
。 它需要一个char**
因为它打算根据手册页设置它指向的char*
:
如果
endptr
不为NULL
,则strtol()
将第一个无效字符的地址存储在*endptr
。
通过传递一个未初始化的char**
,当它试图取消引用它时,你会调用未定义的行为。 将代码更改为:
char *ptr; // Place to put the end ptr
long ret = strtol(temp, &ptr, 10); // Pass address of the location it can set
或者,如果你从不使用ptr
,只需:
long ret = strtol(temp, NULL, 10); // Don't care about end ptr; strtol won't set on NULL
char **ptr;
long ret = strtol(temp, ptr, 10);
是错的。 ptr
没有初始化,也没有引用任何有用的东西。
strtol()
的第二个参数必须引用存储第一个未转换字符的地址的实际char *
值的地址。 根据7.22.1.3 C标准的strtod,strtof和strtold函数 :
如果endptr不是空指针,则指向最终字符串的指针存储在endptr指向的对象中。
正确的代码将是
char *endptr;
long ret = strtol(temp, &endptr, 10);
要么
char *endptr;
char **ptr = &endptr;
long ret = strtol(temp, ptr, 10);
在这种情况下,在调用strtol()
, endptr
的值将是未转换为结果long
值的第一个字符的地址。
如果您不关心第一个未转换的字符是什么:
char **ptr;
long ret = strtol(temp, NULL, 10);
在这里你已经分开了你的字符串。 所以每个字符串包含一个长数字。 第二个参数用于了解转换在字符串中停止的位置。 如果您不需要它,请传入NULL
char *temp = strdup(part[1]);
long ret = strtol(temp, NULL, 10);
printf("%lx\n", ret);
此外, printf
需要不同的格式标志。 这里lx
为long hexadecimal
。
函数strtol(const char *str, char **str_end, int base);
将取消引用str_end
并执行类似*str_end = one_after_end_of_parsed_long
; 因此,当您传递char**
类型的指针时,它不会指向可由strtol
修改的有效指针对象,那么您将产生未定义的行为。
你宁愿写
char *ptr; // space for taking on a pointer value
long ret = strtol(temp, &ptr, 10);
或(不是首选变体):
char **ptr = malloc(sizeof(char*));
long ret = strtol(temp, ptr, 10);
...
free(*ptr);
根本不需要使用strtok()
,因为strtol()
将第二个参数指向的指针设置为指向解析后的数字后面的字符。
一个完整的示例程序:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
long *number = NULL;
size_t numbers = 0;
size_t numbers_max = 0;
char *curr, *next, *ends;
long temp;
size_t i;
while (1) {
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len < 1)
break;
curr = line_ptr;
ends = line_ptr + line_len;
numbers = 0;
while (1) {
/* Parse next long. */
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break;
if (next == curr)
break;
/* Need to grow number array first? */
if (numbers >= numbers_max) {
size_t temp_max = (numbers | 1023) + 1025 - 16;
long *temp_ptr;
temp_ptr = realloc(number, temp_max * sizeof number[0]);
if (!temp_ptr) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
numbers_max = temp_max;
number = temp_ptr;
}
/* Save parsed number. */
number[numbers++] = temp;
/* Skip trailing whitespace, */
curr = next;
while (curr < ends && (*curr == '\t' || *curr == '\n' || *curr == '\v' ||
*curr == '\f' || *curr == '\r' || *curr == ' '))
curr++;
/* Skip separator. */
if (*curr == '|')
curr++;
else
break; /* No separator, so that was the final number. */
}
printf("Parsed %zu longs:", numbers);
for (i = 0; i < numbers; i++)
printf(" %ld", number[i]);
printf("\n");
fflush(stdout);
}
if (ferror(in)) {
fprintf(stderr, "Error reading standard input.\n");
exit(EXIT_FAILURE);
}
free(line_ptr);
line_ptr = NULL;
line_max = 0;
free(number);
number = NULL;
numbers = 0;
numbers_max = 0;
return EXIT_SUCCESS;
}
除可用内存外,该程序没有限制。 行长度或它在数组中存储的数字量。 数字数组的增长政策很时髦(只是我的风格); 随意用你喜欢的任何东西替换它。 只需确保temp_max
至少为numbers + 1
。 使其更大意味着您一次分配更多,因此减少“慢”realloc()调用。
外部while
循环遍历从标准输入读取的行。
内部while
循环解析该行的long,由管道符号|
分隔 。 strtol()
忽略前导空格。 如果数字和后面的管道字符之间有空格,我们需要明确地跳过它; 你也可以使用just while (curr < ends && isspace(*curr)) curr++;
为了那个原因。
如果要将所有长numbers = 0;
收集到单个数组中,而不是每行,只需省略numbers = 0;
在内部while
循环之前。 (并在外部while
循环后移出打印数字。)
实际转换,
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break; /* errno == ERANGE; number too large in magnitude! */
if (next == curr)
break; /* end of input, no number */
依赖于如果要转换的数字的数量太大, strtol()
将设置errno = ERANGE
并返回LONG_MIN
(如果字符串中的数字为负数)或LONG_MAX
(如果为正数)。 要检测到这一点,我们必须先将errno
为零。 如果字符串为空(或者行中有一个迷路\\0
字符, \\0
,则strtol()
将返回0, next == curr
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.