-1

I'm having trouble understanding the behavior of strtol() in C. For example, when I'm testing the string " 4396", if I have:

char *test = " 4396";
char *ptr;
int result = strtol(test, &ptr, 10);
printf("%d\n", result);
printf("%d\n", strlen(ptr));

Output:

4396
//not 0

I'm just wondering about why the length isn't 0, since I have checked to the end already?

Thank you for your help.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Ioce
  • 19
  • 4
  • 2
    `ptr` is an uninitialized pointer, what does it have to do with `strtol`? Calling `strlen(ptr)` is simply undefined behavior – UnholySheep Feb 08 '19 at 23:08
  • 1
    Also, `strtol` returns a `long int` – P Varga Feb 08 '19 at 23:11
  • Yes, I'm sorry for the mistake. And also from what I've learned from school they all uses int for storing the value. – Ioce Feb 08 '19 at 23:16
  • 3
    After the edit I cannot reproduce the result: https://ideone.com/MtRXaR (though it might be due to using the wrong type specifiers?) – UnholySheep Feb 08 '19 at 23:21
  • 4
    [`strlen` returns `size_t`](https://en.cppreference.com/w/c/string/byte/strlen) so printing with `%d` is also UB. [Use `%zu` instead](https://stackoverflow.com/q/940087/995714) – phuclv Feb 08 '19 at 23:22
  • 1
    (as you have edited your question) After consuming `" 4396"`, `ptr` is set to the next character after the last digit converted, e.g. after the `'6'` which is pointing at the *nul-character* ending `test`. So `ptr` points to the *empty-string* and will have `strlen` equal `0`. You can confirm with `if (!*ptr) puts ("pointing at nul-character");` – David C. Rankin Feb 08 '19 at 23:22

2 Answers2

1

strtol() stores a pointer to the character after the last digit in " 4396", hence a pointer to the null terminator of this string constant.

The length of the C string pointed to by ptr is 0 returned by strlen() with type size_t.

It is very surprising to see no output for the statement printf("%d\n", strlen(ptr));. Here are some hints:

  • One possible explanation is strlen() returns a size_t and you pass that as the argument for %d which expects an int. Passing an incompatible type, such as size_t which can have a different size, has undefined behavior. You should either use the C99 conversion specification printf("%zu\n", strlen(ptr)); or convert the length to unsigned long for portability to legacy C libraries: printf("%lu\n", (unsigned long)strlen(ptr));

  • Another possible explanation is you did not include <string.h> and strlen() is inferred to return type int, which is different from the actual type returned... Undefined behavior as well.

  • You did not post a full program, so the problem might lie somewhere else, in code you did not post.

  • Last but not least: you corrected the code in the initial post and made the question irrelevant. ptr was an uninitialized pointer, passing to strlen() had undefined behavior, probably causing the program to stop, which would explain why you did not get any output.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

the following proposed code:

  1. cleanly compiles
  2. performs the desired functionality
  3. note the corrections to several statements
  4. note the addition of the header files
  5. note the addition of the function: main()
  6. in programming, details count

and now the proposed code:

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

int main( void )
{
    char* test = " 4396";
    char *ptr;
    long result = strtol(test,&ptr,10);  // note the change from `int` to `long`
    printf("%ld\n",result);  // note the change from'%d' to '%ld'
    printf("%zu\n",strlen(ptr)); // note the change from '%d' to '%zu'
}

the output of the proposed code is:

4396
0

Note the second output is 0 because strlen() does not count the trailing NUL byte and ptr points to the NUL byte

user3629249
  • 16,402
  • 1
  • 16
  • 17
  • Although implicit since C99, it is advisable to add the `return 0;` explicitly. Defining `test` as a `const char *` would also avoid warnings with `-Wall`. – chqrlie Feb 09 '19 at 09:31
  • @chqrlie on Clion and Mac Os X with the following flags, it doesn't warn me to make `const` the `char*`, `set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64 -std=c11 -pedantic -O2 -Wall -Wextra -Wshadow -Wpointer-arith \ -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -pthread")` – Soner from The Ottoman Empire Feb 09 '19 at 09:37
  • @chqrlie how can I utilize it to warn me ?? – Soner from The Ottoman Empire Feb 09 '19 at 09:42
  • @snr: I do get a warning on OS/X with `clang`, but the I have `-Wwrite-strings` in my `CFLAGS` variable. The specific warning tag is `-Wincompatible-pointer-types-discards-qualifiers`. Systematic `const` qualification of pointer arguments (aka *constification*) improves code quality and avoids subtile bugs. – chqrlie Feb 09 '19 at 09:46
  • @chqrlie `-Wwrite-strings` works. Would you mind sharing your flags with me since I'd like to be as possible as sure writing code in secure? – Soner from The Ottoman Empire Feb 09 '19 at 09:50
  • 1
    @snr: compiler flags vary from one project to another because of compiler and platforms specifics... On OS/X, I routinely use `clang -O3 -Weverything -Werror -Wno-padded -Wno-shorten-64-to-32 -Wno-missing-prototypes -Wno-vla -Wno-missing-noreturn -Wno-sign-conversion -Wno-unused-parameter -Wwrite-strings -funsigned-char` but these warnings are sometimes too strict for advanced projects. I then use `clang -O3 -Wall -Werror -Wwrite-strings -Wchar-subscripts -funsigned-char` – chqrlie Feb 09 '19 at 11:18