0

Please read until the end before you say: "Oh no, this question again..."

I am right now seating in a C course and the following example has been provided in course book:

#include <stdio.h>
#include <stdint.h>

void Terminal_PrintData(uint16_t * const Data);

int main(void){
    uint16_t StringData[] = "MyData";
    Terminal_PrintData(StringData);
}

void Terminal_PrintData(uint16_t * const Data)
{
    printf("Value: %s", *Data);
}

When I compile this masterpiece, this is what I get:

F:\AVR Microcontroller>gcc -o test test.c
test.c: In function 'main':
test.c:7:26: error: wide character array initialized from non-wide string
  uint16_t StringData[] = "MyData";

My questions are:

  • Is it correct to declare a string with uint16_t?
  • What is recommended way to pass a string to a function?
Bracketz
  • 307
  • 5
  • 10
  • Use char arrays (or uint8_t arrays) instead of uint16_t – Mido Nov 11 '15 at 11:42
  • 2
    First you need to figure out that an (unprefixed) string literal is a pointer to an array of *characters*. Then you need to understand what the unary `*` operator (dereference) does, because you're using it wrong. – Some programmer dude Nov 11 '15 at 11:44
  • @JoachimPileborg: It is not just a pointer for array-initialisation. How else would the compiler know the length for something like `char arr[] = "Hello";`? – too honest for this site Nov 11 '15 at 11:46
  • @Mido: When using string literals, one normally should not use `uint8_t`, but `char` or `(un)signed char` to clarify you use it as characters, not integers. Even worse ist that `char` has not necessarily 8 bits as `uint8_t`. – too honest for this site Nov 11 '15 at 11:48
  • Formally there are no "strings" in C. – alk Nov 11 '15 at 11:57
  • @JoachimPileborg - Did you read the example is in a C course book? – Bracketz Nov 11 '15 at 12:31
  • 1
    @alk False. There are strings, cf. ISO 9899:2011 §7.1.1 ¶1 which defines what a string is. – fuz Nov 11 '15 at 12:35
  • @FUZxxl: Fair enough ... :} I should have written "*... is no "string" type ...*" – alk Nov 11 '15 at 12:39
  • 1
    @Bracketz Thanks for accepting my answer. However, I've spotted some errors in my answer, regarding `const` qualifiers. I've updated my answer, and recommend you take a (brief) look at it. –  Nov 11 '15 at 13:53
  • Here a good example on how to do different things with strings and pointers in C https://stackoverflow.com/a/46344713/5842403 – Joniale Sep 22 '17 at 08:58

3 Answers3

6

Your immediate questions:

  • Is it correct to declare a string with uint16_t?

No.

All strings are always char[]. There ar also wide strings (strings of wide characters), which have the type wchar_t[], and are written with an L prefix (e.g. L"hello").

  • What is recommended way to pass a string to a function?

As a pointer to the first character in the string, so either char * or wchar_t *. The const qualifier makes no sense here; it would mean that the pointer (not the string itself) is constant (see here). I'd recommend writing wchar_t const * or const wchar_t * instead; it has a different meaning (i.e. the string can't be changed), but it's correct and meaningful.


You mention that this code is from a course book. I'm not sure whether you mean that these are lecture notes handed out by your professor, or whether you bought a published book. In either case, if this snippet is representative of the quality of the book/notes, get a refund.

For now, let's make this code work.

There's an error in this code that will cause the program to crash:

  • printf("... %s ...", ..., *string, ...) when string is a char* is always wrong. Don't dereference that pointer.

If you insist on using "wide" characters (which is questionable), you're going to have to change a few things:

  • Rather than using char, you need to include <wchar.h> and use wchar_t instead. As far as I know, uint16_t and uint8_t won't work unless you use explicit casting everywhere. That's because char doesn't have 8 bits, but CHAR_BIT bits.
  • Wide character literals must start with an L prefix.
  • Rather than using printf, you need to use wprintf.
  • In the format string, use %ls rather than %s. (Unless you use Microsoft's compiler.)

Finally, some less grave errors:

  • As noted, T * const arguments are useless. The author probably meant T const * (or equivalently const T *).
  • You can remove <stdint.h> and <stdio.h>. We're no longer using uint16_t, and <wchar.h> declares some wide character <stdio.h>-like functions.

I end up with the following code:

#include <wchar.h>

void Terminal_PrintData(wchar_t const * Data);

int main(void){
    wchar_t StringData[] = L"MyData";
    Terminal_PrintData(StringData);
}

void Terminal_PrintData(wchar_t const * Data)
{
    wprintf(L"Value: %ls", Data);
}

This compiles and runs as expected with both GCC and Clang on Linux x86-64.

Community
  • 1
  • 1
3

There are two kinds of strings: "non-wide" ones which consist of chars and "wide" ones which consist of wchar_ts and are written as L"" (single wchar_ts can be written as L''). There are functions to convert between them; apart from these, you cannot intermix them.

Depending on the system, a wchar_t can be 16 or 32 bits wide.

glglgl
  • 89,107
  • 13
  • 149
  • 217
0

You should view your string as an array of characters (in this case, 8-bit characters) so a uint8_t would suffice. What you normally do is pass the beginning of the string (which is the pointer to the array) to your function. To make it safer, you could also pass the length of the string as an argument, but normally your string will end with a delimiter (\0).

when you pass stringData to your function, you're actually saying &stringData[0], literally "the address (&) of the first element ([0]) of the array".

Evert
  • 563
  • 1
  • 5
  • 13