Re: Efficient way of building strings

Go To Last Post
31 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am writing a driver to interface with a 3rd party piece of hardware. The object responds to AT commands.

Edit: compiler: AVRGCC (WinAvr)

In a header file, I have defined a bunch of commands. I then use those definitions to send commands to the device. The issue is that some of the commands have variable arguments.

Example: AT+XYZWB=, where is the number of bytes to write.

I am trying to write a function where the user would call it with a buffer and a length(int). This is what I have:

#define XYZ_WR_BIN		"AT+XYZWB="

uint8_t sendBinMsg(uint8_t *buf, uint16_t len) {
	uint8_t ret = 0;
	char lenStr[4] = {'\0','\0','\0','\0'};
	char binCmd[13];

	if((len < 1) || (len > 340)) {
		fprintf_P(&comExt_str, PSTR("ERROR - sendBinMsg(): invalid length.\n"));
	}
	else {
		sprintf(lenStr, "u", len); 
		//prep first command
		strncpy(binCmd, XYZ_WR_BIN, sizeof(XYZ_WR_BIN));
		binCmd[sizeof(XYZ_WR_BIN)] = '\0';
		strncat(binCmd, lenStr, 3);
		binCmd[sizeof(binCmd) - 1] = '\0';
		sendCommand(binCmd, 1); //tx cmd to device
	}
	return(ret);
}

General question: This seems a little clunky (like most of my code). Is there a more elegant way?

A few specific questions:

1. in the strncpy line, would it be better for the third argument to be sizeof(binCmd)? As it currently is, this could lead to an overrun if the constant changes to some longer value. If I change it, what happens when strncpy gets to the end of the constant argument (which is NOT null terminated)? Should this be resolved by:

#define XYZ_WR_BIN      "AT+XYZWB=\0" 

2. Does sprintf automatically null terminate the generated string? I.e., is the explicit declaration of the variable lenStr[4] as all nulls unnecessary?

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
General question: This seems a little clunky (like most of my code). Is there a more elegant way?

sprintf(binCmd, XYZ_WR_BIN "%u", len);
sendCommand(binCmd, 1);

Quote:
If I change it, what happens when strncpy gets to the end of the constant argument (which is NOT null terminated)?
What do you mean? A string literal has an implicit null termination.

Quote:
Does sprintf automatically null terminate the generated string?
Yes.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the tip. I did not realize that one could concatenate strings in a sprintf statement.

When I write code, it sometime seems like trying to sculpt David with a sledgehammer.

To make sure I understand:

If sprintf automatically appends a termination to the string binCmd, then binCmd must be as long as the longest expected string (i.e., a 3 character number for len) + 1 (for the termination char), correct?

What happens to the last few chars in binCmd if the len variable is only one character:

binCmd : AT+XYZWB=1nn

What are the "nn"? Does it matter? If I follow this with a printf(&comExt_str, binCmd);, the printf stops at the null, and discards the last two characters, correct?

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sprintf does not concatenate strings. You use strcat for that.

Jerry

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't currently have hardware to try this on yet, but it looks like Stefan is suggesting implies that sprintf concatenates the two strings.

When I tried it on my pc (Cygwin/gcc), it works flawlessly -- i.e., the concatenation takes place as Stefan suggested.

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hobbss wrote:
I don't currently have hardware to try this on yet, but it looks like Stefan is suggesting implies that sprintf concatenates the two strings.

When I tried it on my pc (Cygwin/gcc), it works flawlessly -- i.e., the concatenation takes place as Stefan suggested.

No, sprintf doesn't do the concatenation, the compiler does. Consecutive string literals are concatenated in C.

char str[] = "Hello"  " "  "world"  "!";
char str[] = "Hello world!";

Both lines are fully equivalent.

Quote:
If sprintf automatically appends a termination to the string binCmd, then binCmd must be as long as the longest expected string (i.e., a 3 character number for len) + 1 (for the termination char), correct?
Yes.

Quote:
What happens to the last few chars in binCmd if the len variable is only one character:

binCmd : AT+XYZWB=1nn

What are the "nn"? Does it matter? If I follow this with a printf(&comExt_str, binCmd);, the printf stops at the null, and discards the last two characters, correct?

Yes. The excess characters merely contain irrelevant junk (actually the same content like before the sprintf).

Stefan Ernst

Last Edited: Tue. Feb 5, 2013 - 10:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jmulchin wrote:
sprintf does not concatenate strings

You probably didn't mean that the way it came out

How can you call this if not concatenate

char my_array[10];
sprintf(my_array,"%s%s","123","456");

After the sprintf the content of my_array="123456"

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stefan, thank you for the clarification.

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sprintf returns the length of the string and you can use that to concatenate subsequent sprintfs. Use snprintf to avoid buffer overflow!

uint8_t index;
char buffer[80];
index=snprintf(&buffer[0],sizeof(buffer),"Hello");
index+=snprintf(&buffer
,sizeof(buffer)-index," World"); index+=snprintf(&buffer
,sizeof(buffer)-index,\n");
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

alexan_e wrote:
jmulchin wrote:
sprintf does not concatenate strings

You probably didn't mean that the way it came out

How can you call this if not concatenate
[snip ......
After the sprintf the content of my_array="123456"

Alex

Ok, I guess you could call this a concatenation of 2 strings. But I view this as building a string when sprintf is used, while I view merging 2 existing zero terminated strings a doing a concatenation. avr-libc defines sprintf as
"Variant of printf() that sends the formatted characters to string s", and is defined as a STDIO type function. Also the word Concatenate is not used to describe sprintf anywhere I know of.

While strcat() is defined as:
"Concatenate two strings."

The strcat() function appends the src string to the dest string overwriting the '\0' character at the end of dest, and then adds a terminating '\0' character. The strings may not overlap, and the dest string must have enough space for the result.

So as I say, just my opinion and in agreement with avr-libc definitions.

Jerry

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Follow up to the string literal comment above:

Consider the following:

void sendMsg(uint8 *buf, uint16 len) {
     //Verify last val is a  (requirement)
     buf[len - 1] = 13;   //0x0D
     fprintf(&comExt_str, buf);
//OR: fprintf(&comExt_str, "s", buf);
//Note: buf most likely is NOT null terminated
}

Which is "better"?

The above assumes that len is less than sizeof(buf). Ignoring that, will the second (commented) fprintf work correctly if there is no explicit (or implicit) null terminating character in buf?

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Note: buf most likely is NOT null terminated
Then this most likely will NOT work. The null termination is required.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

alexan_e wrote:
jmulchin wrote:
sprintf does not concatenate strings

You probably didn't mean that the way it came out

How can you call this if not concatenate

char my_array[10];
sprintf(my_array,%:%s","123","456");

After the sprintf the content of my_array="123456"

Alex

It should be pointed out that the use of the above argument where using sprintf with fixed constants like "123" and "456" is not very useful, because the compiler would be a better place to to assign my_array as my_array[10] = {"123456"}; instead. The use of the sprintf function, or any function that uses formatted output is better served with actual variables. If you know ahead of time what the outcome is going to be, then let the compiler do the work. And I still would not call the above concatenation. It's really string formatting with parameter substitution.

Jerry

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jmulchin wrote:
Ok, I guess you could call this a concatenation of 2 strings. But I view this as building a string when sprintf is used, while I view merging 2 existing zero terminated strings a doing a concatenation

So you think that sprintf can't merge two existing null terminated strings, is that what you are saying?
strcat() will find the null termination position of the first string while with sprintf you have to find it yourself, so how do we go from that to can't concatenate?

jmulchin wrote:

It should be pointed out that the use of the above argument where using sprintf with fixed constants like "123" and "456" is not very useful, because the compiler would be a better place to to assign my_array as my_array[10] = {"123456"}; instead. The use of the sprintf function, or any function that uses formatted output is better served with actual variables. If you know ahead of time what the outcome is going to be, then let the compiler do the work. And I still would not call the above concatenation. It's really string formatting with parameter substitution.

I don't see the point of this comment, I just gave you a simple example that shows the concatenation and didn't use variables to save space.
I could have provided an example using variables (even mixed arrays and integers) changed at run-time in place of the constants I have used , would that make a difference in what I tried to show, I don't think so.

Here are some more examples of using sprintf to concatenate strings, taken from http://stackoverflow.com/questio...

sprintf(Buffer,"Hello World");
sprintf(Buffer + strlen(Buffer),"Good Morning");
sprintf(Buffer  + strlen(Buffer),"Good Afternoon");

or

int length = 0;
length += sprintf(Buffer+length, "Hello World");
length += sprintf(Buffer+length, "Good Morning");
length += sprintf(Buffer+length, "Good Afternoon");

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Ok, I guess you could call this a concatenation of 2 strings.

In short: Just because sprintf does not explicitly mantion "concatenate" does not mean it can not be used for concatenating two null-terminated strings.

Still, using it forces code and execution time in for interpreting the format string. strcat() does not do that.

Note that Stefan makes use of the implicit concatenation of two adjacent string literals:

#define XYZ_WR_BIN      "AT+XYZWB=" 
sprintf(binCmd, XYZ_WR_BIN "%u", len);

becomes
sprintf(binCmd, "AT+XYZWB=%u", len);[/code]

So.. There are several slightly different concatenations discussed here.

Quote:
What happens to the last few chars in binCmd if the len variable is only one character:

binCmd : AT+XYZWB=1nn

What are the "nn"? Does it matter?


The "nn" are likely unchanged. It does not matter.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
Note: buf most likely is NOT null terminated
Then this most likely will NOT work. The null termination is required.

An interesting side discussion going on regarding the semantics of the C standard.

However, trying to keep on track:

Koschi: which one will not work? The first or second (commented) line in my example? I have tested both on my pc (gcc compiler, cygwin environment), but just because it worked with that particular compiler doesn't make it "right". What is the correct way to handle this situation? Perhaps this is a weird case. In a program running on a pc (and thus potentially vulnerable to malicious code), I think that the

printf(&comExt_str, "s" buf);

line is safer/better, which would require me to guarantee that buf ends with a null terminator. On a 8 bit microcontroller, where I am not too worried about malicious code, I think that

printf(&comExt_str, buf);

is better, because I do not need to guarantee that buf ends with a null terminator (or do I?).

P.S. The whole "percent sign not allowed" thing is really annoying.

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

P.S. The whole "percent sign not allowed" thing is really annoying.

Use % wherever you want a % sign to appear.

I'm a bit lost your last post uses printf() but as a "destination" appears to be specified before the format string you surely mean sprintf()?

Why not simply copy/paste your Cygwin+gcc test programs (replacing % with % where necessary)?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, I apologize for the confusion. I should have copied the code rather than retyping it. It should be "fprintf" not "printf", and reflect code running on my AVR.

The question I am trying to ask (and doing a poor job of it), is what is the best practice for this? I can easily get something that "just works", but I would like to write good code that is as portable as possible. In general, I have several strings that may or may not be null terminated that I need to combine and send to a single device (i.e., UART).

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
In general, I have several strings that may or may not be null terminated that I need to combine

You will need to treat strings with termination and strings without without termination in different ways. For strings without termination there must be another way of knowing their length. I might be missing it but I see no mention above on how the length of unterminated strings are known.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh I see _str means "stream" while I thought it meant "string". Perhaps an unwise choice of name? ;-)

Anyway to summarise what I think you maybe learned from this thread:

1) The C compiler will always concatenate adjacent CONSTANT strings. So you can do:

#define FOO "hello "
#define BAR "world"
printf(FOO BAR);

because it's the equivalent of:

printf("hello ""world");

which the compiler concatenates to be:

printf("hello world");

when it passes that to printf() (it is not printf() itself doing the concat - replace printf() with a do nothing foo() then examine the setup code that calls foo() and you'll stil see "hello world" being passed.

2) C has a strong habit of adding zero termination to strings

char text[] = "hi";

creates a 3 byte array holding 'h', 'i' and 0x00. Similarly

char text[5] = "hi";

creates a 5 byte array and sets [0] to 'h', [1] to 'i' and [2] to 0x00. The only way to avoid a zero byte (NUL) being added is with:

char text[2] = "hi";

where you specifically don't allow enough room for the 0x00 to be held.

3) almost anything that deals with strings in C uses 0x00 as the end mark for strings. This includes printf(), strcpy(), strcat(), etc.

4) if you have a "dynamic" string (that is one who's content is not known at compile) time you cannot rely on implicit concatentation:

char buf[10];
strcpy(buf, "hello ");
printf(buf "world");

will not work. You have to join the strings programmatically:

char buf[10];
strcpy(buf, "hello ");
strcat(buf, "world");
printf(buf);

This works because strcat() scans along the buf[] that already contains "hello " looking for NUL and starts adding "world" from that byte onwards. It would be disastrous if buf[] did not contain NUL at that point as strcat() would jst carry on chomping it's way through memory until it happened upon something that did hold 0x00 then it would write "world" there.

5) because of this potential danger it's advised not to:

char buf[6] = "hello ";
strcat(buf, "world");
printf(buf);

(no room for NUL there so strcat() is going to get confused) and in fact modern thinking is that strcat(), strcpy() etc should not be used as they don't police lengths. Far better to use strncpy(), strncat() which limit the operation to "n" bytes which can be kept within the bounds of the destination though it makes things more complex to invoke them as you need to know what "n" is.

6) strlen() can be useful in concatentation as you can do things like:

char buf[10];
strcpy(buf, "hello ");
sprintf(&buf[strlen(buf)], "world");

in this strlen(buf) is the length of "hello " (ie how many bytes until the 0x00 NUL is found) so it says:

sprintf(&buf[6], "world");

where buf[6] currently holds the NUL from the strcpy()

7) as further noted above (s)printf() returns the bytes written so you could also do something kind of similar with:

char buf[10];
int len;
len = sprintf(buf, "hello ");
sprintf(&buf[len], "world");

or drop the array syntax with:

sprintf(buf+len, "world");

So you have several options but it does depend on what you are concatenating how you approach this. implicit concatentation works for XYZ_WR_BIN because its a string who's length is known at compile time. But if you have "char * buf" passed into a routine and want to concatentate onto that you either have to use the fact that its length is also passed or, if you can guarantee it's NUL terminated a strlen/strcat style search for the NUL so they know where to continue from.

Only pass pointer+length if the data is not a "C string". There seems little point otherwise, you might as well just pass the pointer UNLESS you want to prevent array over-runs in which case don't pass the current length but the maximum permissible length then use the strN*() functions to police the writing of the string so nothing writes beyond the allocated space.

8) Err..

9) ..I think that's everything

I'm not sure this actually answers your original question but maybe rounds up the "tools in the toolbox" so you know what's available to solve it based on the kind of data you are trying to join.

Oh and an aside. If you are using C strings with ASCII characters in them then use "char" don't use uint8_t/unsigned char and don't use int8_t/signed char or you will be plagued with "pointer differs in signedness" warnings everywhere. There are three types of char and the str*()family use "char" that is neither signed nor unsigned.

Finally:

char lenStr[4] = {'\0','\0','\0','\0'}; 
...
strncat(binCmd, lenStr, 3); 

Do you maybe see why this is not going to do what you think it does? If not read this:

http://www.cplusplus.com/referen...

specifically:

Quote:
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.

Guess what:

strlen(lenStr);

is (and it's not 4).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good point.

I have a string literal (?) from a #define statement -- this is of known length. I need to concatenate a number (converted to a string) that may be 1, 2, or 3 chars long (i.e., 1 < number < 1000) to this string literal. Therefore, I know the maximum length the concatenated string can be.

#define BASE_CMD  "STRING"

uint8_t number = 100 //(or 10, or 1...)

fprintf(&comExtstr, BASE_CMD "%u", number);

//Want to transmit: "BASE_CMD100"

In this example, the maximum length is 8+3+1 (1 for the null terminator?). It may be as short as 8+1+1 (depending on the size of "number").

Edit: Cliff posted a marvelous response while I was typing this. Thanks for taking the time to explicitly explain the issue. I understand (for now...).

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to add to point(1) above:

#include 
#include 
#include 
#include 
#include 

void foo(char * str);

int main(void) {
	foo("hello ""world");
}

generates:

.LC0:
	.string	"hello world"
	.section	.text.startup,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB2:
	.file 1 ".././ledblink.c"
	.loc 1 9 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 10 0
	ldi r24,lo8(.LC0)	 ; ,
	ldi r25,hi8(.LC0)	 ; ,
	call foo	 ; 

Note that the compiler has concatenated the strings as .LC0 and just passes a pointer to that to the call to foo(). For the curious foo() was implemented as:

void foo(char * str) {
}

So it's not foo() (or printf or sprintf or fprintf) doing the concatenation here and it is not passed separate poiinters to "hello " and "world".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

[NB: I actually fixed it in his post above so the point is not side-tracked by % signs but I'll leave this as it's worth explaining - and by editing in this comment I just lost the point you made - hopefully now fixed again...]

Quote:
fprintf(&comExtstr, BASE_CMD "%u", number);

% needs a semicolon at the end to work , it is like % so you need to write

Quote:
fprintf(&comExtstr, BASE_CMD "%;u", number);

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The only way to avoid a zero byte (NUL) being added is with:

Code:
char text[2] = "hi";

where you specifically don't allow enough room for the 0x00 to be held.


NEVER do this. The null terminator will still be added, but to the memory cell after the char array. If anything else meaningful is there it will be destroyed. If anything else meaningful is allocated to that spot and written after your code above your string will loose its zero terminator.

What is your aversion towards zero terminated strings?

You really (as in REALLY) need to pick up a good text on C and read the part about string handling.

Re the original question on how string catenation is best done: IMOit is best done in the way that maes the code most readable. You will notice later if you need to sacrifice this for optimization. If you do not notice, you do not need to optimize.

Write it as straight-forward as possible and on to the next problem instead of drilling down on this.

Again- for the strings you have that are not zero-terminated:
- Where do they come from?
- How do you know their length? (Constant length?, Length in separate variable? or?)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I have a string literal (?) from a #define statement -- this is of known length. I need to concatenate a number (converted to a string) that may be 1, 2, or 3 chars long (i.e., 1 < number < 1000) to this string literal.

If you want to avoid the printf overhead, heres one way (very setchy - not tested):

#define STRING_LITERAL "A String Literal"

void StringAndIntToString( const char * s, int i, char result)
{
   char iAsString[10];
   itoa(i, iAsString, 10);
   strcpy(result,s);
   strcat(result, iAsString);
)
   
int main(void)
{
   int seven = 7;
   int fourtytwo = 42;
   
   char catenatedResult[50];
   
   StringAndIntToString(STRING_LITERAL, seven, catenatedResult); // -> "A String Literal 7"
   
   StringAndIntToString(STRING_LITERAL, fourtytwo, catenatedResult); // "A String Literal 42"
   .
   .
   .

All strings in this example - be they literal or computed at run time - are zero terminated.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am not opposed to null terminators. I am simply trying to write some functions that will be used by a third person. The arguments for the functions are a char * and a length variable. I would like my functions to be robust enough to handle implementation by a user who may be as ignorant of strings as I was.

I respectfully disagree with your "on to the next problem" suggestion. While I am primarily a hardware engineer, I do end up writing quite a bit of software. I am merely trying to get a firm understanding of this particular issue (which I think I now have). As to getting a good text on C, you are probably right. However, my shelf is currently too full with antenna theory and wave propagation books. Have pity on a former RF engineer currently writing low level embedded drivers!

Edit:

One last scenario:

char tempArray[] = {97, 98, 99, 100, 101};

Will this get a null terminator automatically? Will the compiler treat it any differently if it was:

uint8_t tempArray[] = {97, 98, 99, 100, 101};

Science is not consensus. Science is numbers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
Quote:

The only way to avoid a zero byte (NUL) being added is with:

Code:
char text[2] = "hi";

where you specifically don't allow enough room for the 0x00 to be held.


NEVER do this. The null terminator will still be added, but to the memory cell after the char array. If anything else meaningful is there it will be destroyed. If anything else meaningful is allocated to that spot and written after your code above your string will loose its zero terminator.
Sorry, but you are wrong. That is explicitly allowed and the null is omitted in that case.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Will this get a null terminator automatically?

No. You get the NUL when "" are involved. Similarly:

char tempArray[] = {'H', 'e', 'l', 'l', 'o'}; 

wont get one either - it'll be a 5 byte array.

But if your user who doesn't know about strings and NULs knows enough to use printf() surely he knows enough to use your functions and doesn't need to worry about passing lengths or anything like that. When you use printf() you use:

printf("hello");

you don't use:

printf("hello", 5);

to tell it there are 5 characters there. It "knows" how long "hello" is because the compiler passed "hello",0x00 and it just keeps going to the 0x00. Even when you implement your own UART routines you do something like this:

void uart_putchar(char c) {
  UDR = c;
}

void uart_putstring(char * str) {
  while(*str) {
    uart_putchar(*str++);
  }
}

and call it with:

uart_putstring("hello world");

or

char buff[PLENTY];
strcpy(buff, "hello ");
strcat(buff, "world");
uart_putstring(buff);

All this works because of:

  while(*str) {

which is doing exactly what printf() and strcpy() and strcat() and everything else based on C-strings is doing and just continuing until 0x00 is encountered (while(0) ends the loop).

So if you are writing C and you want to use strings then just use C-strings. That is some non-0 characters followed by one or more 0x00 to mark the end and usually initialised with "".

(if using C++ you have std::string which is a far more thorough implementation)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Sorry, but you are wrong. That is explicitly allowed and the null is omitted in that case.

Well-well.. You learn something new every day. Today I even had to de-learn a wrong and then learn a right. Since the statement is by 'sternst' I will not even double-check.

My apologies for any confusion.

Quote:

The arguments for the functions are a char * and a length variable.

Then replace strncat() for the strcat() in my example above, take the length as a parameter to the function and pass it to strncat().

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Since the statement is by 'sternst' I will not even double-check

But you doubted it when I originally said it? :shock: .... :wink:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No - I simply missed it, Cliff. I grovel in your general direction.. :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]