How would I send this string one character at a time? - c

Here is the code that I have. I am unable to test it right now, so I'm just wondering if someone can verify that this will take the full str string and send each character individually to TXREG
char str [10];
int n;
n = sprintf(str, "%u%% %u\n", Duty_Cycle, DAC_Output);
transmit_uart(str); // Send the value
And here is the transmit_uart() method.
void transmit_uart(const char *value) {
for(int i = 0; value[i] != '\0'; i++) {
while(TXIF == 0) {}
TXREG = value[i];
}
}
So this should send something like
50% 128
Every time I call transmit_uart() with a string formatted the way I have it up there.
UPDATE: I was able to test it yesterday, and this did in fact work! Thanks for all the help!

Although I haven't personally loaded it onto an MCU and tested it, yes, that looks fine as long as TXIF does what it looks like.
You really should use snprintf or a larger buffer. This is one case where overflowing the integer (as in simply having too large a value, or any negative value) would cascade into buffer overflow.

If you want to send string of 5-9 characters:
1 or 2 or 3 symbols - Duty_Cycle value,
1 symbol - % symbol,
1 symbol - space,
1 or 2 or 3 symbols - DAC_Output value,
1 symbol - \n symbol,
you are doing right.

Related

Count Different Character Types In String

I wrote a program that counts and prints the number of occurrences of elements in a string but it throws a garbage value when i use fgets() but for gets() it's not so.
Here is my code:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int main() {
char c[1005];
fgets(c, 1005, stdin);
int cnt[26] = {0};
for (int i = 0; i < strlen(c); i++) {
cnt[c[i] - 'a']++;
}
for (int i = 0; i < strlen(c); i++) {
if(cnt[c[i]-'a'] != 0) {
printf("%c %d\n", c[i], cnt[c[i] - 'a']);
cnt[c[i] - 'a'] = 0;
}
}
return 0;
}
This is what I get when I use fgets():
baaaabca
b 2
a 5
c 1
32767
--------------------------------
Process exited after 8.61 seconds with return value 0
Press any key to continue . . . _
I fixed it by using gets and got the correct result but i still don't understand why fgets() gives wrong result
Hurray! So, the most important reason your code is failing is that your code does not observe the following inviolable advice:
Always sanitize your inputs
What this means is that if you let the user input anything then he/she/it can break your code. This is a major, common source of problems in all areas of computer science. It is so well known that a NASA engineer has given us the tale of Little Bobby Tables:
Exploits of a Mom #xkcd.com
It is always worth reading the explanation even if you get it already #explainxkcd.com
medium.com wrote an article about “How Little Bobby Tables Ruined the Internet”
Heck, Bobby’s even got his own website — bobby-tables.com
Okay, so, all that stuff is about SQL injection, but the point is, validate your input before blithely using it. There are many, many examples of C programs that fail because they do not carefully manage input. One of the most recent and widely known is the Heartbleed Bug.
For more fun side reading, here is a superlatively-titled list of “The 10 Worst Programming Mistakes In History” #makeuseof.com — a good number of which were caused by failure to process bad input!
Academia, methinks, often fails students by not having an entire course on just input processing. Instead we tend to pretend that the issue will be later understood and handled — code in academia, science, online competition forums, etc, often assumes valid input!
Where your code went wrong
Using gets() is dangerous because it does not stop reading and storing input as long as the user is supplying it. It has created so many software vulnerabilities that the C Standard has (at long last) officially removed it from C. SO actually has an excellent post on it: Why is the gets function so dangerous that it should not be used?
But it does remove the Enter key from the end of the user’s input!
fgets(), in contrast, stops reading input at some point! However, it also lets you know whether you actually got an entire line of of text by not removing that Enter key.
Hence, assuming the user types: b a n a n a Enter
gets() returns the string "banana"
fgets() returns the string "banana\n"
That newline character '\n' (what you get when the user presses the Enter key) messes up your code because your code only accepts (or works correctly given) minuscule alphabet letters!
The Fix
The fix is to reject anything that your algorithm does not like. The easiest way to recognize “good” input is to have a list of it:
// Here is a complete list of VALID INPUTS that we can histogram
//
const char letters[] = "abcdefghijklmnopqrstuvwxyz";
Now we want to create a mapping from each letter in letters[] to an array of integers (its name doesn’t matter, but we’re calling it count[]). Let’s wrap that up in a little function:
// Here is our mapping of letters[] ←→ integers[]
// • supply a valid input → get an integer unique to that specific input
// • supply an invalid input → get an integer shared with ALL invalid input
//
int * histogram(char c) {
static int fooey; // number of invalid inputs
static int count[sizeof(letters)] = {0}; // numbers of each valid input 'a'..'z'
const char * p = strchr(letters, c); // find the valid input, else NULL
if (p) {
int index = p - letters; // 'a'=0, 'b'=1, ... (same order as in letters[])
return &count[index]; // VALID INPUT → the corresponding integer in count[]
}
else return &fooey; // INVALID INPUT → returns a dummy integer
}
For the more astute among you, this is rather verbose: we can totally get rid of those fooey and index variables.
“Okay, okay, that’s some pretty fancy stuff there, mister. I’m a bloomin’ beginner. What about me, huh?”
Easy. Just check that your character is in range:
int * histogram(char c) {
static int fooey = 0;
static int count[26] = {0};
if (('a' <= c) && (c <= 'z')) return &count[c - 'a'];
return &fooey;
}
“But EBCDIC...!”
Fine. The following will work with both EBCDIC and ASCII:
int * histogram(char c) {
static int fooey = 0;
static int count[26] = {0};
if (('a' <= c) && (c <= 'i')) return &count[ 0 + c - 'a'];
if (('j' <= c) && (c <= 'r')) return &count[ 9 + c - 'j'];
if (('s' <= c) && (c <= 'z')) return &count[18 + c - 's'];
return &fooey;
}
You will honestly never have to worry about any other character encoding for the Latin minuscules 'a'..'z'.Prove me wrong.
Back to main()
Before we forget, stick the required magic at the top of your program:
#include <stdio.h>
#include <string.h>
Now we can put our fancy-pants histogram mapping to use, without the possibility of undefined behavior due to bad input.
int main() {
// Ask for and get user input
char s[1005];
printf("s? ");
fgets(s, 1005, stdin);
// Histogram the input
for (int i = 0; i < strlen(s); i++) {
*histogram(s[i]) += 1;
}
// Print out the histogram, not printing zeros
for (int i = 0; i < strlen(letters); i++) {
if (*histogram(letters[i])) {
printf("%c %d\n", letters[i], *histogram(letters[i]));
}
}
return 0;
}
We make sure to read and store no more than 1004 characters (plus the terminating nul), and we prevent unwanted input from indexing outside of our histogram’s count[] array! Win-win!
s? a - ba na na !
a 4
b 1
n 2
But wait, there’s more!
We can totally reuse our histogram. Check out this little function:
// Reset the histogram to all zeros
//
void clear_histogram(void) {
for (const char * p = letters; *p; p++)
*histogram(*p) = 0;
}
All this stuff is not obvious. User input is hard. But you will find that it doesn’t have to be impossibly difficult genius-level stuff. It should be entertaining!
Other ways you could handle input is to transform things into acceptable values. For example you can use tolower() to convert any majuscule letters to your histogram’s input set.
s? ba na NA!
a 3
b 1
n 2
But I digress again...
Hang in there!

Function to Split a String into Letters and Digits in C

I'm pretty new to C, and I'm trying to write a function that takes a user input RAM size in B, kB, mB, or gB, and determines the address length. My test program is as follows:
int bitLength(char input[6]) {
char nums[4];
char letters[2];
for(int i = 0; i < (strlen(input)-1); i++){
if(isdigit(input[i])){
memmove(&nums[i], &input[i], 1);
} else {
//memmove(&letters[i], &input[i], 1);
}
}
int numsInt = atoi(nums);
int numExponent = log10(numsInt)/log10(2);
printf("%s\n", nums);
printf("%s\n", letters);
printf("%d", numExponent);
return numExponent;
}
This works correctly as it is, but only because I have that one line commented out. When I try to alter the 'letters' character array with that line, it changes the 'nums' character array to '5m2'
My string input is '512mB'
I need the letters to be able to tell if the user input is in B, kB, mB, or gB.
I am confused as to why the commented out line alters the 'nums' array.
Thank you.
In your input 512mB, "mB" is not digit and is supposed to handled in commented code. When handling those characters, i is 3 and 4. But because length of letters is only 2, when you execute memmove(&letters[i], &input[i], 1);, letters[i] access out of bounds of array so it does undefined behaviour - in this case, writing to memory of nums array.
To fix it, you have to keep unique index for letters. Or better, for both nums and letters since i is index of input.
There are several problems in your code. #MarkSolus have already pointed out that you access letters out-of-bounds because you are using i as index and i can be more than 1 when you do the memmove.
In this answer I'll address some of the other poroblems.
string size and termination
Strings in C needs a zero-termination. Therefore arrays must be 1 larger than the string you expect to store in the array. So
char nums[4]; // Can only hold a 3 char string
char letters[2]; // Can only hold a 1 char string
Most likely you want to increase both arrays by 1.
Further, your code never adds the zero-termination. So your strings are invalid.
You need code like:
nums[some_index] = '\0'; // Add zero-termination
Alternatively you can start by initializing the whole array to zero. Like:
char nums[5] = {0};
char letters[3] = {0};
Missing bounds checks
Your loop is a for-loop using strlen as stop-condition. Now what would happen if I gave the input "123456789BBBBBBBB" ? Well, the loop would go on and i would increment to values ..., 5, 6, 7, ... Then you would index the arrays with a value bigger than the array size, i.e. out-of-bounds access (which is real bad).
You need to make sure you never access the array out-of-bounds.
No format check
Now what if I gave an input without any digits, e.g. "HelloWorld" ? In this case nothin would be written to nums so it will be uninitialized when used in atoi(nums). Again - real bad.
Further, there should be a check to make sure that the non-digit input is one of B, kB, mB, or gB.
Performance
This is not that important but... using memmove for copy of a single character is slow. Just assign directly.
memmove(&nums[i], &input[i], 1); ---> nums[i] = input[i];
How to fix
There are many, many different ways to fix the code. Below is a simple solution. It's not the best way but it's done like this to keep the code simple:
#define DIGIT_LEN 4
#define FORMAT_LEN 2
int bitLength(char *input)
{
char nums[DIGIT_LEN + 1] = {0}; // Max allowed number is 9999
char letters[FORMAT_LEN + 1] = {0}; // Allow at max two non-digit chars
if (input == NULL) exit(1); // error - illegal input
if (!isdigit(input[0])) exit(1); // error - input must start with a digit
// parse digits (at max 4 digits)
int i = 0;
while(i < DIGITS && isdigit(input[i]))
{
nums[i] = input[i];
++i;
}
// parse memory format, i.e. rest of strin must be of of B, kB, mB, gB
if ((strcmp(&input[i], "B") != 0) &&
(strcmp(&input[i], "kB") != 0) &&
(strcmp(&input[i], "mB") != 0) &&
(strcmp(&input[i], "gB") != 0))
{
// error - illegal input
exit(1);
}
strcpy(letters, &input[i]);
// Now nums and letter are ready for further processing
...
...
}
}

Is there a better way of modifying arrays from within a function in C?

I am trying to create a formatted string , however I do not know why I cannot print global array which I have modified inside the function.Also the strange behavior is that I cannot access only a specific global array (rand_session_key) rest of the other global arrays are behaving as normal(similar operations are being done on them except their size varies) and I can access their value properly. This code is run on an esp32 (DOIT Dev Kit V1) (with Arduino-Core) , when I run this program on my computer (modifying a few functions etc.) the result is what I expect , I think I am overlapping the characters in the memory or accessing it the wrong way , but had it been the case I would not have yielded the expected output on my computer.
I tried to modify my program and made it more verbose. Also I ran the same code (with some obvious modifications to make it run on my computer) , and the result is good as expected.
char persistent_peripheral_id[] = "FRUCTOSE96";
char rand_session_iden[7] = {'\0'};
char rand_session_key[17] = {'\0'};
char rand_session_channel[3] = {'\0'};
char *generate_random_session_identifier(char *rand_session_iden_local)
{
srand(time(NULL));
int counter = 0;
for (counter = 0; counter < 6; counter++)
*(rand_session_iden_local + counter) = (random(10) % ('~' - ' ')) + 'k';
rand_session_iden_local[counter] = '\0';
printf("Identifier : %s\n", rand_session_iden); //acessing global defintion of array everything is good until here
return &rand_session_iden_local[0];
}
char *generate_random_session_key(char *rand_session_key_local)
{
srand(time(NULL));
int counter = 0;
for (counter = 0; counter < 16; counter++)
*(rand_session_key_local + counter) = (random(10) % ('~' - ' ')) + 'b';
rand_session_key_local[counter] = '\0';
printf("Key : %s\n", rand_session_key);//acessing global defintion of array everything is good until here
return &rand_session_key_local[0];
}
char *generate_random_session_channel(char *rand_session_channel_local)
{
srand(time(NULL));
int channel_value = random(100);
sprintf(rand_session_channel_local, "%03ld", channel_value);
printf("Channel : %s\n", rand_session_channel);//acessing global defintion of array everything is good until here
return &rand_session_channel_local[0];
}
void begin_exchange_package()
{
//If this does not works here (observe rand_session_key) , it will not work for sprintf also ??
printf("\n %s-%s-%s-%s \n", (char *)persistent_peripheral_id,
generate_random_session_identifier(rand_session_iden),
generate_random_session_key(rand_session_key),
generate_random_session_channel(rand_session_channel));
//Notice it prints here ????
printf("\n %s \n",generate_random_session_key(rand_session_key));
Serial.println("Done");
//sprintf((char *)plain_text_package, "{\"p\":\"%s\",\"r\":\"%s\",\"k\":\"%s\",\"c\":\"%s\"}", (char *)persistent_peripheral_id,(char *)rand_session_iden, (char *)rand_session_key , (char *)rand_session_channel);
}
void setup()
{
Serial.begin(115200);
begin_exchange_package();
}
void loop()
{
}
The Output is
FRUCTOSE96-tnltkp--094
Where I expected all the 4 arrays to be printed ?? but it does print separately , is my array being terminated in the wrong way ?? also the logic to assign a random character will always yield a printable ASCII Character (I learned this from a forum on esp32's website)
This code ...
sprintf(rand_session_channel_local, "%03ld", channel_value);
... requires rand_session_channel_local to point to an array of at least four characters, because at will print at least three digits plus a string terminator. The array into which it points, rand_session_channel, is only three characters long. The resulting behavior is undefined.
The observed manifestation of the UB is consistent with the global arrays being laid out in memory such that rand_session_key immediately follows rand_session_channel, such that overflowing the latter means that the string terminator is written to position 0 of the former, making it an empty string. Note, however, that you cannot rely on predicting manifestations of UB, nor is it generally of much use to analyze them. Instead, avoid exercising UB.
It's unclear what random function you are using, since the C standard library's does not take an argument, but if the argument to yours specifies an exclusive upper bound then you could just change the sprintf format to "%02ld". Alternatively, increase the size of rand_session_channel to at least 4.

what is this "continue" does exactly?

I have this part of the code, I don't understand. I know "continue" in if, skips that statement. But why? It supposed to print out an array like " 073.45 * C". The i=2 is for the numbers, right? what does Temp_s[5-i}=result%10+0x30 do? Can someone explain this code please.
void Convert_data_to_temp(unsigned long data)
{
unsigned long result;
unsigned char i=0;
result=((33*1000*data)/4096)%100000;//rounding off to maximum 5 digits
Temp_s[0]=0x30;
Temp_s[3]='.';
for(i=0;i<6;i++)
{
if(i==2){continue;}
Temp_s[5-i]=(result%10)+0x30;
result=result/10;
}
Temp_s[i]=32;
Temp_s[i+1]=32;
Temp_s[i+2]='*';
Temp_s[i+3]=32;
Temp_s[i+4]='C';
Temp_s[i+5]=13;
Temp_s[i+6]=10;
}
Thanks
Well that's a nice mess for sure. Here's what this code does (assuming there's a Temp_s char array in scope that has at least 13 elements).
void Convert_data_to_temp(unsigned long data)
{
unsigned long result;
unsigned char i=0;
// Calculate... something.
// Apparently the calculation is done in fixed-point decimal,
// with 3 decimal places (hence `*1000`).
// Also, the comment is wrong: that's not rounding off, the result will wrap.
// In any case, we can be sure that 0 <= result < 100000.
result=((33*1000*data)/4096)%100000;//rounding off to maximum 5 digits
Temp_s[0]=0x30; // ASCII for '0'
Temp_s[3]='.';
// Now Temp_s looks like this (? represents an indeterminate value:
//
// 0 ? ? . ? ? ? ? ? ? ? ? ?
// Now we're filling Temp_s backwards from the 5th index,
// with the respective digits of `result`. The `continue` skips over
// index 3 so we don't overwrite the '.' we just put there.
for(i=0;i<6;i++)
{
if(i==2){continue;}
Temp_s[5-i]=(result%10)+0x30; // Again, 0x30 is just ASCII '0'.
result=result/10;
}
// Let's say that result was 12345. Now Temp_s looks like this:
//
// 1 2 3 . 4 5 ? ? ? ? ? ? ?
// And now we fill the rest of Temp_s with these hard-coded values.
// Note that we retrieve i at the value it was left by the for, i.e. 6.
Temp_s[i]=32; // 32 is an ASCII space
Temp_s[i+1]=32;
Temp_s[i+2]='*';
Temp_s[i+3]=32;
Temp_s[i+4]='C';
Temp_s[i+5]=13; // ASCII Carriage return
Temp_s[i+6]=10; // ASCII Line feed
// In the end, Temp_s looks like this:
//
// 1 2 3 . 4 5 [space] [space] * [space] C \r \n
}
Apparently the code is broken, too: the computation of result hints at 3-decimals fixed-point, but the representation ends up with only two decimals, and overwrites the '0' that was assigned at the very beginning.
I suggest you just throw that crazy code away and use the tried-and-true standard library:
snprintf(
Temp_s, sizeof Temp_s,
"%.3lu.%.2lu * C\r\n",
result / 100, result % 100
);
The code as a whole converts a 5-digit decimal number such as 54321 into "543.21 * C\r\n" — except that it doesn't ensure that the string is null terminated. However, if the target array Temp_s is a global variable and is big enough and is only written to by this function, then probably there is a null at the end already, but it is simpler and safer to make sure.
The assignment Temp_s[0]=0x30; could be dropped, and the loop could be written more clearly as:
for (i = 0; i < 6; i++)
{
if (i == 2)
Temp_s[5-i] = '.';
else
{
Temp_s[5-i] = (result % 10) + '0';
result /= 10;
}
}
strcpy(&Temp_s[6], " * C\r\n"); // Adds null termination
Frankly, though, it could (and maybe should) be written as a call to sprintf() (which also ensures that the string is null terminated):
int dp = result % 100;
int un = result / 100;
sprintf(Temp_s, "%.3d.%.2d * C\r\n", un, dp);
You could write instead (noting that result is an unsigned long, hence the change of format conversion specifier):
sprintf(Temp_s, "%.3lu.%.2lu * C\r\n", result / 100, result % 100);
It would be preferable to be able to use snprintf(), but it isn't clear how this global variable is declared, and using sizeof(Temp_s) might not be correct:
snprintf(Temp_s, sizeof(Temp_s), "%.3lu.%.2lu * C\r\n", result / 100, result % 100);
That's weird. Looks like you're iterating over that for loop, but you're not executing the code for i == 2. The continue statement sends you to the next iteration of the for loop before you do anything.
That'd the kind of code that'd really benefit from a well-placed comment..
Temp_s[5-i]=(result%10)+0x30;
Writes at index 5-i of the array Temp_s
The result modulo 10 + 48.
The continue skips the statements in the loop and goes back to the i++ statement and after that to the predicate of the loop. But only if i is equal to two.
All in all this should give you the result calculated before as a string representation.
The if i==2 preserves that you do not overwrite the '.'.

Some questions concerning a C integer to string function

Whilst reading through K&R, I came across the integer to string function. I gave it a quick read, and decided to implement it myself, but instead of printing, it updates a character array.
Here is what I have
void inttostr(int number, char str[]) {
static int i;
if (number / 10) {
inttostr(number / 10, str);
}
str[i++] = number % 10 + '0';
}
It seemed to work for the few integers I gave it, but I have some questions.
I haven't explicitly included the nul byte \0 at the end, so why does the string work fine when printed with printf("%s\n", str);?
I don't think I'm very good at thinking recursively. When I try and step through the program in my mind, I lose track of what is still awaiting execution. Is there a better way of seeing what is happening internally, to help me learn?
Any other suggestions on the code?
I'm using Xcode.
This is not homework. I'm just learning.
Thanks!
You're correct that you're never writing NUL, which is a bug.
In general, you don't have to think through the entire solution. You just have to make sure every step is correct. So in this case, you say:
1 . inttostr(number / 10, str);
will take care of all but the last digit.
2 . Then I will take care of the last one.
You can trace what's happening, though. For e.g. 54321 it looks like:
inttostr(54321, str); // str = ...;
inttostr(5432, str); // str = ...;
inttostr(543, str); // str = ...;
inttostr(54, str); // str = ...;
inttostr(5, str); // str = ...;
str[0] = '5'; // str = "5...";
str[1] = '4'; // str = "54...";
str[2] = '3'; // str = "543...";
str[3] = '2'; // str = "5432...";
str[4] = '1'; // str = "54321...";
Note that when you don't return from any of the functions until you write the first character, then you return in the opposite order from the calls.
The ... signifies that you haven't NUL-terminated. Another issue is that you're using a static variable, so your code isn't reentrant; this means it breaks in certain scenarios, including multi-threading.
To address the reentrancy and NUL issue, you can do something like the code below. This creates a helper function, and passes the current index to write.
void inttostr_helper(int number, char str[], int *i)
{
if (number / 10) {
inttostr_helper(number / 10, str, i);
}
str[(*i)++] = number % 10 + '0';
str[*i] = '\0';
}
void inttostr(int number, char str[])
{
int i = 0;
inttostr_helper(number, str, &i);
}
EDIT: Fixed non-static solution.
I am impressed of the creativity to use recursive, despite that it is not necessary. I think the code should remove statically-allocated i variable because this variable will persist through calls. So the second time you use this function from your code, e.g. from main(), it will not be initiated and will be the same value from previous call. I would suggest using return value as follow:
int inttostr(int number, char *str) {
int idx = 0;
if (number / 10) {
idx = inttostr(number / 10, str);
}
str[idx++] = number % 10 + '0';
return idx;
}
1, Your compiler (especially in debug mode) may have filled str with 0. Unix does this if you allocate memory with new() - But don't rely on this, either set the first byte to \0 or memset everything to 0
2, paper + pencil. Draw a table with each variable across the top and time down the side.
3, Write the simplest, longest version first, then get clever.
I haven't explicitly included the nul byte \0 at the end, so why
does the string work fine when printed
with printf("%s\n", str);?
how is the original char array declared when you call your function the first time? if it is a static char array then it will be filled with 0's and that would explain why it works, otherwise its just "luck"
I don't think I'm very good at thinking recursively. When I try and
step through the program in my mind, I
lose track of what is still awaiting
execution. Is there a better way of
seeing what is happening internally,
to help me learn?
honestly, I am not sure if anybody is good in thinking recursively :-)
Did you try drawing the execution step by step?

Resources