0
#include <pthread.h>
#include "../header/tlpi_hdr.h"

static void *
threadFunc(void *arg)
{
    char *s = (char *) arg;

    printf("%s", s); 

    return (void *) strlen(s);
}

int
main(int argc, char *argv[])
{
    pthread_t t1; 
    void *res;
    int s;

    s = pthread_create(&t1, NULL, threadFunc, "Hello world\n");
    if (s != 0)
        errExitEN(s, "pthread_create");

    printf("Message from main()\n");
    s = pthread_join(t1, &res);
    if (s != 0)
        errExitEN(s, "pthread_join");

    printf("Thread returned %ld\n", (long) res);

    exit(EXIT_SUCCESS);
}

Above code is just what I'm studying now, but I can't understand about conversion of data type.

On threadFunc(), the return type is void *. But from this function, strlen(s) is type size_t. But how this can be changed to (void *). I mean, why not (void *) &strlen(s) ?

And, also in the main(), there is printf("Thread returned %ld\n", (long) res);.

From this, variable res is the type (void *), not int or long. So, it should be changed like this:

(long) *res.

But, how this can be changed to (long) res?

allen
  • 59
  • 2
  • 10
  • 1
    *"Why not (void *) &strlen(s)"* doesn't even compile (and it doesn't make sense regardless), so you can obviously rule that out as viable. Short answer: the author is essentially shoving a non-pointer value into a pointer with the hopes it will "fit" (both coming and going). The proper way to do this is via [`intptr_t`](http://en.cppreference.com/w/c/types/integer) values, but apparently they weren't looking for correctness. – WhozCraig Aug 01 '16 at 06:45
  • The cast says "do as I say; I know what I am doing". It is dangerous if you don't know what you are doing. It also says "treat the value returned by `strlen()` as if it was a `void *`" — make sure it the value is the right size (it usually is already). The technique used works under reasonable assumptions, but is hardly elegant. I'd not be surprised to find it is treading on thin ice with regards to 'undefined behaviour', either, but I can't immediately put a finger on the source of the trouble. – Jonathan Leffler Aug 01 '16 at 06:59
  • You're asking how to return an arbitrary value from the thread function? – 2501 Aug 01 '16 at 08:02

3 Answers3

0

Here strlen returns an integer which is cast to void * pointer and later back to integer in printf call.

Regarding conversions between pointers and integers, C99 standard only guarantees that void * pointer can be converted to uintptr_t or intptr_t integer and then back to void * pointer and the result will compare equal to the original pointer, if those optional types are defined (Reference).

The conversion done here is different and not guaranteed to work, but since nearly all modern computers internally represent pointers as integers, it will generally work as long as integer is of same size as void * pointer.

So if you know how your compiler handles this kind of conversion, then you can do something like this, but in general this should not be done.

Proper way to return integer value would be to e.g. allocate space for the integer value, and then return pointer to it.

Community
  • 1
  • 1
Markus Laire
  • 2,538
  • 2
  • 14
  • 22
  • 2
    "Since pointers are internally just integers, casting any integer (which has same or smaller size as pointer) to pointer and back does work." - Pointers are **not** integers! Conversion between pointer and integer is implementation dfined and is not guaranteed "to work" by the standard. – too honest for this site Aug 01 '16 at 07:11
  • @Olaf Pointers **are internally** just integers. – Markus Laire Aug 01 '16 at 07:17
  • 2
    @MarkusLaire Please provide the citation from the Standard that defines pointers representation to be the same as integer repres. – 2501 Aug 01 '16 at 07:18
  • @MarkusLaire: Tis is wrong with respect to the C standard. How the CPU processes the data is not relevant. They e.g. could be easily hashes into a database (actually they are - the most simple way). – too honest for this site Aug 01 '16 at 07:24
  • @Olaf C standard guarantees that `void *` -> `uintptr_t` -> `void *` conversion is lossless which implicitly tells that pointers can be represented as integers. (But `uintptr_t` -> `void *` -> `uintptr_t` is not guaranteed as far as I know, so I'll think how to edit my answer.) – Markus Laire Aug 01 '16 at 07:37
  • @MarkusLaire But is `uintptr_t` guaranteed to exist for all architectures? – tangrs Aug 01 '16 at 08:06
  • I rewrote whole answer: @tangrs no, those types are optional – Markus Laire Aug 01 '16 at 08:06
  • The real risk (danger, whatever you want to call it) is the value for what is returned by `strlen`. Consider a general pointer value `0x00007ffff74b3eb0` now `strlen ("foo")` returns `3`. Where do you think the pointer value of `0x3` points? (somewhere deep in a **system reserved** block of memory). What happens if you try and reference your `strlen` pointer? (*hint*: begins with *Seg* and ends with *ault*) So it is the value you are attempting to create a pointer of that posses the most risk, regardless of whether you *can do it* or not. – David C. Rankin Aug 01 '16 at 08:17
  • @DavidC.Rankin true, but as long as it's not used as a pointer, it doesn't matter where it points to – Markus Laire Aug 01 '16 at 08:21
  • As long as you in no way reference the pointer, it's a no harm/no foul situation. You just have to understand the inherent danger to your code of having a pointer (with the address and key to the nuclear Seg Fault Bomb dangling around in your code. One false move -- and it will end quickly. – David C. Rankin Aug 01 '16 at 08:31
  • @MarkusLaire: There is no `uinptptr_t` here. Left that apart, the standard **only** defines that the conversion **from a pointer** to that type and back **to the same pointer type** equals the original pointer. It need not be lossless, e.g. the LSB can be removed and reapplied if all pointers of that type are aligned. Still left that latter apart, this question converts an integer **to** a pointer and back to a **different** integer type. That definitively is not defined by the standard. – too honest for this site Aug 01 '16 at 08:50
  • Oh, and C standard is C11 **only**, not C99! Read the foreword. – too honest for this site Aug 01 '16 at 08:50
  • @DavidC.Rankin: It is not typical for C to provide a safety-net for user code. The use always has to be aware not to invoke UB by missusing values, etc. (e.g. allocate the correct size for a `struct` object, etc.) That is the lesser problem. But there might be cases `size_t` is larger than `void *` (not likely, though, but still an issue), or integers cannot be converted 1:1 to a pointer or back. E.g. reserved bits in the pointer value - 68000 Macs or Amigas had an issue here IIRC as the upper 8 bits were missused for tagging, segmented addresses like for 8086 16 bit mode or other CPUs, etc. – too honest for this site Aug 01 '16 at 09:01
  • @MarkusLaire: The question is about C, not C++. And C implies standard C. If there already is an answer, flag the question as dup. Otherwise don't like other answer as sole reference. That's what the standard is for. FYI: http://port70.net/~nsz/c/c11/n1570.html – too honest for this site Aug 01 '16 at 09:05
  • And whatever you call "modern computer": Far by most architectures C is used on are not x86 (not sure if that's what you'd call a modern arch; I'd actually not - bloat complexity is not a synonym for modern), nor ARM or other 32 bit CPUs. – too honest for this site Aug 01 '16 at 09:07
  • @Olaf I'd link to actual standard, but I havn't been able to find C99 anywhere online. – Markus Laire Aug 01 '16 at 09:11
  • @MarkusLaire: I repeat: **C99 is not C standard**. No matter whether you found it online or not!! (Sorry, but I don't believe you even tried to find it. A simple google search for "c standard" showed up a stack overflow question with a list of links to all standards, the drafts are free and identical in all relevant aspects) and some other links. – too honest for this site Aug 01 '16 at 09:17
0

But how this can be changed to (void *). I mean, why not (void *) &strlen(s)(?)

The return type of strlen() is size_t, some unsigned integer type. Casting an arbitrary integer value to void* is undefined behavior (UB).

return (void *) strlen(s); // Undefined behavior

Code could save the length somewhere, say in static/global or allocated memory and return the address. I have doubts this would meets OP's other coding goals.

static size_t len = 0;
len = strlen(s);
return (void *) &len;

The return value from strlen() 1) cannot have its address taken with &strlen(...) 2) Even if the language allowed such, code still needs to consider the lifetime of the pointer.

how this can be changed to (long) res?

It is important that the calling code interprets the returned pointer as the same type to prevent UB.

void *res;
...
s = pthread_join(t1, &res);
if (s != 0) {
  errExitEN(s, "pthread_join");
}
if (res != NULL)
  size_t *len_ptr = (size_t*) res;
  printf("Thread returned %zu\n", *len_ptr);
  // or if long is truly needed
  long len_as_long = (long) *len_ptr;
  printf("Thread returned %ld\n", len_as_long);
}
chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
-1

You cannot return (void *) &strlen(s) because you will get an error. What you want to do is var = strlen(s); return (void *) &var;. But in fact this is not correct because you are returning a pointer to a variable that will be destroyed at the end of the function, so your pointer will be pointing to an empty or overrided part of memory. It's a little strange what pthread function is doing, because you get the value of the position 12. This is a non sense thing, except to check that pthread was running. And strlen returns size_t, that can be an int or short o something like that, according to What is size_t in C?

Community
  • 1
  • 1
Alexi
  • 389
  • 1
  • 8
  • Returning the address of a local variable is a really bad idea already. Dereferencing that address invokes undefined behaviour. – too honest for this site Aug 01 '16 at 07:12
  • Yes, and that's I said: "(return the address of var)... is not correct because you are returning a pointer to a variable that will be destroyed..." – Alexi Aug 01 '16 at 07:15
  • @Olaf The pointer to the non-existing local variable has an indeterminate value, which can be a trap value. Accessing the pointer is enough to cause ub. – 2501 Aug 01 '16 at 07:17
  • Also `size_t` cannot be `int` nor `short`. It is an unsigned type and shall not be `unsigned short` anyway. The conversion as shown in the question is nevertheless possibly legal and does not invoke UB like your proposal does. – too honest for this site Aug 01 '16 at 07:18
  • @2501: Using the value of the pointer is allowed. You just must not dereference it. E.g. you can `printf` it. The value is not indeterminate, but just not valid anymore. – too honest for this site Aug 01 '16 at 07:21
  • @Olaf You're mistaken Olaf. I can back my statement with a citation from the C11 Standard: 6.2.4 2: *The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.* – 2501 Aug 01 '16 at 07:23
  • @Olaf. When I say var=strlen(s), of course I didn't put all the code, I supose that there is a line above that says "int var;" or something like that, so it is not a non-existing variable. It's a varaible that only exists while function exists. And size_t is a integer or short or long, or whatever, it depends of the architecture. For example, I have this in my stddef.h: "#define __SIZE_TYPE__ long unsigned int" and "typedef __SIZE_TYPE__ size_t;". In this case, for my architecture, size_t is an unsigned long. – Alexi Aug 01 '16 at 07:26
  • @2501: Oh, thanks (thzere was some discussion here about that some time ago). But that does not change the rest. – too honest for this site Aug 01 '16 at 07:26
  • @Alexi: Would you please read comment carefully and have a look into the standard! `size_t` cannot be `int` nor `short`! – too honest for this site Aug 01 '16 at 07:28
  • @Olaf I'm just curious, why can't size_t be unsigned short? – 2501 Aug 01 '16 at 07:31
  • @Olaf If you mean that cannot be a signed type, of course you are right, I only talking about the size (number of bits). size_t always has to be unsigned, of course. – Alexi Aug 01 '16 at 07:33