Size_t and buffer in print function - c

I have the following function to print out text to an LCD. Everything works, although I'm having some trouble understanding how it works and why it's written this way.
I understand it takes a string array, and loops through it to write each character to the LCD.
What I don't understand is why does it use a buffer instead of just accessing the str variable directly.
And why does it return the length of the string that was written out.
Also why does my write function also need to return size_t.
// This will print character string to the LCD
size_t print(const char str[]) {
if (str == NULL) return 0;
const uint8_t *buffer = (const uint8_t *)str;
size_t size = strlen(str);
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;

why does it use a buffer instead of just accessing the str variable directly?
Because the write() function takes a uint8_t parameter, but str contains char. So this avoids having to write a cast in the write() call. It would work just as well using
if (write((uint8_t)*(str++))) n++;
but this is harder to read.
why does it return the length of the string that was written out?
So the caller will know how many characters were successfully written, in case write() reports an error in the middle of the string (by returning 0).
why does my write function also need to return size_t?
I can't think of a reason, other than for consistency with functions. write() should just return either 1 when it writes successfully or 0 when it fails.

Please checkout the code changes below with the comments
size_t print(const char *str) { // As it always degrades into a pointer!
if (str == NULL) {
return 0;
} // Always use braces - do not wish to get you pants down!
// Is this needed? const uint8_t *buffer = (const uint8_t *)str;
// Do not need this - counting a string twice ! size_t size = strlen(str);
// Not requires dsize_t n = 0;
//while (size--) {
for (size_t n = 0; str[n]; n++) {
write(buffer[n]); //if (write(*buffer++)) n++;
// else break; for loop removes the requirement
}
return n;
}
Please note write is a part of unidstd see unistd - So some confusion here

Related

checking function of strncpy with assert in c language

I need to write c program to compare two strings without using strncpy() and then in another funtion check whether it works with using assert()
In my code by checking with pointers assert(*str2 == *"comp"); I only check the first letter and nothing else.So even there is another word starting with c, it will work.
char initialize_n(char str1[], char str2_n[], int n)
{
//initialize a string from the (at most) n first characters of another string
int i = 0;
for(i = 0; i < n; i++)
{
str2_n[i] = str1[i];
}
str2_n[i] = '\0';
}
void test_initialize_n(){
char str1[100] = "computer";
char str2[100];
initialize_n(str1, str2, 4);
assert(*str2 == *"comp");
}
How to correctly check it with assert?
So, without gcc extensions you can't put this into the assert body because the functions that would do so are forbidden to you.
What you need to do is write your own compare function and call it. Skeleton as follows:
int prefix_equals(const char *left, const char *right, int nleft, int nright)
{
if (nleft != nright) return 0;
/* Use the same for loop here you have in initialize_n */
if (left[i] != right[i]) return 0;
return 1; /* If you got all the way through they're equal */
}
assert(prefix_equals(str2, "comp", 4, 4));
In my experience the most useful form of this actually has two length arguments to save the caller from some ugliness in the call point. It seems wrong at assert level but it isn't wrong when you get to a few thousand lines.

c function convert "fffoootoo" to "foto" (leaves out following repeating characters)

The task would be to remove following characters that are repeating from a char array, like "deeeciddeee" -> "decide" or "phhhonne" -> "phone".
I have a function that crashes the console, and I can't spot the bug:
char* my_unique(char *first, char *last) {
char* ret=first;
for(int i=0; first+i!=last; i++){
if(first[i]==first[i+1]){
for(int j=i; first+j!=last; j++)
first[j]=first[j+1];
last--;
}
}
return ret;
}
it is called this way:
char* a="oooat";
a=my_unique(a, a+strlen(a));
cout<<a;
please help me!
Besides a small bug (you should add the line i--; after last--;, because you're deleting the character at possition i, so what has been the character at i+1 became the new character at possition i. If you don't decrease i, it will be increased and you jump over a character) the code runs perfectly fine IF it is called with
const char* b = "oooat";
char* a = new char[strlen(b) + 1];
for (size_t c = 0; c < strlen(a) + 1; c++) { a[c] = b[c]; }
a = my_unique(a, a + strlen(a));
cout << a;
delete[] a;
Notice that I've used a edit-able copy of the string, as the literal itself is of type const char* and therefor can't be changed at all. And as I said, this works perfectly fine and prints "oat", just as expected, without any crash. So your problem might be that you try to edit a const string literal? In that case you might consider to copy it, as I did, or use std::string (if you code in C++).
There are many beginner mistakes in the code.
Let me point you one by one.
char* a="oooat";
a=my_unique(a, a+strlen(a));
cout<<a;
When you declare a string like this : char* a="oooat", a is a string literal. The memory for the string is allocated into text section of the program. Which basically means you cannot modify the values inside the strings. You can only read from them. Hence when you are passing pointer a to the function and modifying it, it will result in segmentation fault(Illegal access to memory).
Why do you need a ret pointer here? char* ret=first;
You are passing a pointer and modifying the value inside it. Hence the new data will be reflected in the calling function and we need not return it explicitly. So, it is redundant.
Overall logic can be simplified as well
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MYSTR "ooooat"
void my_unique(char *first, char *last) {
int size = last - first;
int i = 0, j = 0, k = 0;
for (; i < size; i++, j++) {
first[j] = first[i];
// Continue to check how many repetitions are there
while (i + 1 < size && (first[i] == first[i+1])) i++;
}
// In the end terminate the string with a NULL.
first[j] = '\0';
return;
}
int main()
{
char a[] = MYSTR;
my_unique(a, a+strlen(a));
printf("%s", a);
return 0;
}
This is in C. There are simpler ways of doing this in C++, and the code can definitely be condensed but has been left simpler for readability.
#include <stdlib.h>
char* fix(char *input) {
char *lookahead = input;
char *newchar, *ret;
// Determine Max Return String Length
int len = 0;
while (*lookahead != '\0') {
len++;
lookahead++;
};
// allocate max possible memory needed and set the pointers
ret = malloc(len);
newchar = ret;
lookahead = input;
*newchar = *lookahead; // copy the first character
while (*lookahead != 0) {
lookahead++; // incrementing this ptr first starts lookahead at 2nd character and
// ensures the null terminator gets copied before the while loop ends
if (*newchar != *lookahead) { // only copy new characters to new return string
newchar++;
*newchar = *lookahead;
};
};
return ret;
};
I'll try to give my answer so that it makes the as little changes as possible to your original code, while using the simplest methods.
The main problem has already been identified by the previous comments - you cannot alter a string literal.
Also, the line of code
i--;
has to be placed as well, with the reason well clarified above.
While making an editable version of the string may be a good way of fixing the problem, a more straightforward way would be to make it a local string, as such :
char b[] = "oooat";
but doing this will make it incompatible with the return type of your my_unique function (char*). But why would you need a return type in the first place, if you are fixing the string itself?
My final code would look like this :
void my_unique(char *first, char *last) {
char* ret=first;
for(int i=0; first+i!=last; i++){
if(first[i]==first[i+1]){
for(int j=i; first+j!=last; j++)
first[j]=first[j+1];
last--;
i--;
}
}
}
making the function return void.
Hope this helps.

Appending char to C array

I have a string declared as such:
char *mode_s = (char *)calloc(MODE_S_LEN, sizeof(char));
How can I add a char to the end of the array?
Lets assume " first available position " means at index 0.
char *mode_s = (char *)calloc(MODE_S_LEN, sizeof(char));
*mode_s='a';
To store a character at an arbitrary index n
*(mode_s+n)='b';
Use pointer algebra, as demonstrated above, which is equivalent to
mode_s[n]='b';
One sees that the first case simply means that n=0.
If you wish to eliminate incrementing the counter, as specified in the comment bellow, you can write a data structure and a supporting function that fits your needs. A simple one would be
typedef struct modeA{
int size;
int index;
char *mode_s;
}modeA;
The supporting function could be
int add(modeA* a, char toAdd){
if(a->size==a->index) return -1;
a->mode_s[index]=toAdd;
a->index++;
return 0;
}
It returns 0 when the add was successful, and -1 when one runs out of space.
Other functions you might need can be coded in a similar manner. Note that as C is not object oriented, the data structure has to be passed to the function as a parameter.
Finally you code code a function creating an instance
modeA genModeA(int size){
modeA tmp;
tmp.mode_s=(char *)calloc(size, sizeof(char));
tmp.size=size;
tmp.index=0;
return tmp;
}
Thus using it with no need to manually increment the counter
modeA tmp=genModeA(MODE_S_LEN);
add(&tmp,'c');
There is no standard function to concatenate a character to a string in C. You can easily define such a function:
#include <string.h>
char *strcatc(char *str, char c) {
size_t len = strlen(str);
str[len++] = c;
str[len] = '\0';
return str;
}
This function only works if str is allocated or defined with a larger size than its length + 1, ie if there is available space at its end. In your example, mode_s is allocated with a size of MODE_S_LEN, so you can put MODE_S_LEN-1 chars into it:
char *mode_s = calloc(MODE_S_LEN, sizeof(*mode_s));
for (int i = 0; i < MODE_S_LEN - 1; i++) {
strcatc(mode_s, 'X');
}
char newchar = 'a'; //or getch() from keyboard
//realloc memory:
char *mode_sNew = (char *)calloc(MODE_S_LEN + 1, sizeof(char));
//copy the str:
srncpy(mode_sNew, mode_s, MODE_S_LEN);
//put your char:
mode_sNew[MODE_S_LEN] = newchar;
//free old memory:
free(mode_s);
//reassign to the old string:
mode_s = mode_sNew;
//in a loop you can add as many characters as you want. You also can add more than one character at once, but assign only one in a new position

What would be a safe and practical way to copy a string as defined by this prompt in C?

The purpose of this function is to copy a string into a "buffer" - essentially another string. However, the problem seems overly complicated than what would be practical.
"Copies at most n-1 characters of string in into the buffer pointed to by
out. If n is reached, returns -2. Otherwise, returns -1 for malformed
input and 0 upon successful completion."
This is what I have:
#include <stdio.h>
#include <assert.h>
int copyStringN(register char *in, register char *out, register int n){
//Declarations
int i; //Dummy index
if(!in || !out) return -1;
for(i=0; i<n; i++){
*out++ = *in++;
}
*out = '\0';
return 0;
}
int main(void){
//Declarations
char in[] = "I'm not trying to inject malicious code...\\x29A.";
const int N = sizeof(in);
char out[N];
int err;
//Main execution
printf("\nThis function will copy at most n-1 characters of string into\nthe buffer pointed to by out.\n\n");
err = copyStringN(in, out, N);
assert(!err);
printf("%s\n", out);
printf("\nPlease press enter to exit...");
getchar();
return 0;
}
This general form was suggested, but it seems overly convoluted than what needs to be done. Why would n ever be reached? The execution should stop before n. Furthermore, wouldn't N = sizeof(in) match the length of the original string?
Personally, I would rather use a function closer to
int copyStringN(register char *in, register char *out)
{
if((!in || !out) && (sizeof(in)<=sizeof(out))) return -1;
else{
while(*t++ = *from++);
return 0;
}
}
int main(void){
//Declarations
char in[] = "I'm not trying to inject malicious code...\\x29A.";
const int N = sizeof(in);
char out[N];
int err;
.
.
.
I believe it would have the same effect with less statements. Let me make this more of a question, how could I write a function that copies a string into another array with the protection defined in the prompt? Also, are the two programs that I presented somehow vulnerable in a way I don't recognize?
Constructive input is appreciated.
This is a strange thing in C.
char mole[] = "mole" is not the same as char *mole = "mole"
I just tried:
char *a1 = "mole";
char a2[] = "mole";
printf ("s1: %i s2:%i\n", sizeof(a1), sizeof(a2) );
a1 is a pointer, so 4 or 8 depending on the architecture.
a2 is an array of size 5.
But you can convert a2 to a char* without warnings. But you loose the size.
Your suggested alternative will not work. (sizeof(in)<=sizeof(out) will always be TRUE, because you are comparing pointers (and not arrays), and they are the same size.
If you want to make safe string copy function, you must always pass output buffer length for size checking, and have means to inform user if input was too long for output.
Edit:
Since people have suggested to use strncpy, I will present safer alternative:
int len = snprintf(output, OUTPUT_SIZE, "%s", input);
if(len < 0 || len >= OUTPUT_SIZE) {
// Failed, handle error
}
Fewer statements in your source does not necessarily imply that it is simpler to grasp. The while row in your alternative solution may work, but is doing too many things at the same time for my taste. You are writing code first for other human beings to read, then for a compiler to read.
I like for example making the NULL and \0 checking explicit.
It is also unclear what you are trying to achieve with the sizeof comparison. Besides comparing size of pointers (instead of intended arrays?), I think you meant || instead of &&. If either pointer is NULL it is an error, whatever the size.
int copyStringN(char *in, char *out)
{
if((in == NULL) || (out == NULL)) {
return -1;
} else {
while(*in != '\0') {
*out++ = *in++;
}
*out = '\0';
return 0;
}
}
The compiled code is probably not going to be much different, only the source is more human readable in my opinion.
Then if there happens to be no '\0' in the in string you are going to have problems. I suppose this is the reason for having a length limit n.
while((*in != '\0') && (n-- > 0)) {
*out++ = *in++;
}
*out = '\0';
Note that you would still be in trouble if n is greater than the size of your arrays and you miss a '\0'.
A very similar interface is strncpy. Perhaps the error modes will make more sense after you read the man page.
It is always better to use strncpy to prevent buffer overflow. char * strncpy ( char * destination, const char * source, size_t num ); Also, better to use strlen rather than sizeof. So, Even if the source string is greater than the destination buffer. It will protect the destination buffer from buffer overflow. I would use n as the maximum size of the destination buffer. And In fact make n = strlen(dest_buffer) -1. To accommodate '\0'.
a basic strncpy would look like:
char *strncpy(char *d,const char *s,int n){int i=0;while(n--&&d[i++]=*s++);return d;}
but you could force a null byte at n-1
char *sstrncpy(char *d, const char *s, int n){
int i=0;
while(--n&&d[i++]=*s++);
if(!n)d[i]=0;
return d;
}

Using read() and write() system calls

So I have an assignment where I'm supposed to use read and write to read in lines from a file from stdin, sort it, then write it out to stdout with write. However, I can't get it to work.
I have to call a sort function on buf first to sort the string (which I got using read) before I re-output it. Can I treat buf as an array? Or does it not work like that? The reason is because I have to sort the string first.
int record_compare(const void *a, const void *b)
{
return (memcmp(a, b, num_bytes));
}
qsort(buf, num_elements, num_bytes, record_compare);
while (count < n - num_bytes)
{
i = memcmp(buf+count, buf+count + num_bytes, num_bytes);
if (i == 0)
count = count + num_bytes;
else
{
for (k = 0; k < num_bytes; k++)
{
printf("%c", buf[count]);
count++;
}
}
}
But since I got the string with read instead of something like fgets, can I still treat buf as an array? This is how the sort works on a normal array (it sorts it then prints out without repeats.
do {
c = read(0, buf+n, 1);
if (c != 0)
n++;
}
while (c != 0);
This is how I got buf.
The read() system call will read N bytes (1 byte at a time the way you've written the code). You will want to read up until you get a newline, or until you get zero bytes returned (which marks EOF), or until you get an error. Assuming you found a newline, you will start a new buffer.
You'll probably have an array of character pointers, and you'll probably allocate a new string for each line, and store the pointer in the array of character pointers.
enum { MAX_LINES = 1024 };
char *lines[MAX_LINES];
int n_lines = 0;
I'm assuming a fixed upper bound is OK; you could arrange to dynamically allocate the array of pointers too.
So, you'll need a function to read a line and store it.
Then you have a problem with your compare function. You'll be calling:
qsort(lines, n_lines, sizeof(char *), record_compare);
But record_compare() will be given two char ** values, so you need to use:
int record_compare(const void *v1, const void *v2)
{
const char *s1 = *(char **)v1;
const char *s2 = *(char **)v2;
return(strcmp(s1, s2));
}
Writing the sorted data is pretty straight forward. So, you need to concentrate on the line reader function.

Resources