struct member character by character in c - c

How can I assign values to struct member character by character. I would like to do something like
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct s
{
char *z;
};
int main ()
{
struct s *ss;
ss = malloc(2 * sizeof *ss);
char *str = "Hello World-Bye Foo Bar";
char *a = str;
int i = 0;
while (*a != '\0') {
if (*a == '-')
i++;
else ss[i].z = *a; // can I do this?
a++;
}
for(i = 0; i<2; i++)
printf("%s\n",ss[i].z);
}
So I can get something as:
ss[0].z = "Hello World"
ss[1].z = "-Bye Foo Bar"
Edit: Forgot to mention, the number of "-" in str might vary.

If const char *str wasn't const you could insert a '\0' to split the string into two. You'd need to shift the other chars to the "right" as well in doing so.
The cleaner solution is to use something like strdup to make two copies of the string, one of which you terminate early, the other of which you start the copy partway through:
e.g.
ss[0].z = strdup(str);
ss[1].z = strdup(strchr(str, '-'));
const size_t fist_part = strlen(str)-strlen(ss[1].z);
ss[0].z[first_part] = 0;
Update: You can use this, even with more than one '-'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct s
{
char *z;
};
int main ()
{
struct s *ss;
ss = malloc(20 * sizeof(struct s));
const char *str = "Hello World-Bye Foo Bar-more-and-more-things";
int i = 1;
char *found = NULL;
ss[0].z = strdup(str);
while ((found = strchr(ss[i-1].z, '-'))) {
// TODO: check found+1 is valid!
ss[i].z = strdup(found+1);
*found = 0;
++i;
}
for(i = 0; i<6; i++)
printf("%s\n",ss[i].z);
return EXIT_SUCCESS;
}
In practice you want to be more careful to avoid bugs with unexpected inputs so you need to be sure you handle:
There is no '-' char
There is no '\0' char
allocation failure
Don't forget to free() too!

You will need to alloc new blocks of memory to hold the split strings (at least the first one).
char *s1, *s2, *a, *b;
const char *str = "Hello World-Bye Foo Bar";
s1 = malloc(strlen(str)+1);
s2 = malloc(strlen(str)+1);
a = str;
int i = 0;
ss[0].z = s1;
b = ss[0].z;
while (*a != '\0') {
if (*a == '-') {
i++;
ss[i].z = s2;
*b = ss[i].z;
*b++ = *a;
} else {
// s[i].z = *a // can I do this? (yes, but it you might not be happy with the result :-)
*b++ = *a; // try this instead...
}
a++;
}

else ss[i].z = *a; // can I do this?
Yes, you can do that. BUT you need to allocate space for each z first ... and do not forget to NUL terminate the strings!
ss = malloc(2 * sizeof *ss);
ss[0].z = malloc(1000); /* don't do it */
ss[1].z = malloc(1000); /* like this! */

Related

Is there an easy way to remove specific chars from a char*?

char * deleteChars = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r"
I have this and i'm trying to remove any of these from a given char*. I'm not sure how I would go about comparing a char* to it.
For example if the char* is equal to "hello," how would I go about removing that comma with my deleteChars?
So far I have
void removeChar(char*p, char*delim){
char*holder = p;
while(*p){
if(!(*p==*delim++)){
*holder++=*p;
p++;
}
}
*holder = '\0';
A simple one-by-one approach:
You can use strchr to decide if the character is present in the deletion set. You then assign back into the buffer at the next unassigned position, only if not a filtered character.
It might be easier to understand this using two indices, instead of using pointer arithmetic.
#include <stdio.h>
#include <string.h>
void remove_characters(char *from, const char *set)
{
size_t i = 0, j = 0;
while (from[i]) {
if (!strchr(set, from[i]))
from[j++] = from[i];
i++;
}
from[j] = 0;
}
int main(void) {
const char *del = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r";
char buf[] = "hello, world!";
remove_characters(buf, del);
puts(buf);
}
stdout:
hello world
If you've several delimiters/characters to ignore, it's better to use a look-up table.
void remove_chars (char* str, const char* delims)
{
if (!str || !delims) return;
char* ans = str;
int dlt[256] = {0};
while (*delims)
dlt[(unsigned char)*delims++] = 1;
while (*str) {
if (dlt[(unsigned char)*str])
++str; // skip it
else //if (str != ans)
*ans++ = *str++;
}
*ans = '\0';
}
You could do a double loop, but depending on what you want to treat, it might not be ideal. And since you are FOR SURE shrinking the string you don't need to malloc (provided it was already malloced). I'd initialize a table like this.
#include <string.h>
...
char del[256];
memset(del, 0, 256 * sizeof(char));
for (int i = 0; deleteChars[i]; i++) del[deleteChars[i]] = 1;
Then in a function:
void delChars(char *del, char *string) {
int i, offset;
for (i = 0, offset = 0; string[i]; i++) {
string[i - offset] = string[i];
if (del[string[i]]) offset++;
}
string[i - offset] = 0;
}
This will not work on string literals (that you initialize with char* x = "") though because you'd end up writing in program memory, and probably segfault. I'm sure you can tweak it if that's your need. (Just do something like char *newString = malloc(strlen(string) + 1); newString[i - offset] = string[i])
Apply strchr(delim, p[i]) to each element in p[].
Let us take advantage that strchr(delim, 0) always returns a non-NULL pointer to eliminate the the null character test for every interrelation.
void removeChar(char *p, char *delim) {
size_t out = 0;
for (size_t in; /* empty */; in++) {
// p[in] in the delim set?
if (strchr(delim, p[in])) {
if (p[in] == '\0') {
break;
}
} else {
p[out++] = p[in];
}
}
p[out] = '\0';
}
Variation on #Oka good answer.
it is better way - return the string without needless characters
#include <string.h>
char * remove_chars(char * str, const char * delim) {
for ( char * p = strpbrk(str, delim); p; p = strpbrk(p, delim) )
memmove(p, p + 1, strlen(p));
return str;
}

How to reverse a string with pointers only?

I'm trying to reverse a string, but it just stays the same. I don't use any modules except <string.h> and <stdio.h>.
void rev(s){
char i, temp;
char *sf = s;
char ri = strlen((s) - 1);
char *sl = &s[ri];
for (i = 0; i < ri; i++){
if (*sf != *sl){
temp = *sf++;
s[i] = *sl--; //
s[ri--] = temp; //those two seems to be getting new characters, but it won't
}
else {
ri--;
sf++;
sl--;
}
}
printf("%s", s);
}
The function will not compile at least because the parameter does not have a type specifier.
void rev(s){
The type char has a little range of acceptable values. So you shall not use it for calculating the length of a string.
The call of strlen in this declaration
char ri = strlen((s) - 1);
invokes undefined behavior. It seems you mean
char ri = strlen(s) - 1;
that also can invoke undefined behavior for an empty string.
This loop
for (i = 0; i < ri; i++){
does not use pointers.
The function can be defined the following way as it is shown in the demonsytrative program below.
#include <stdio.h>
#include <string.h>
char * reverse( char *s )
{
if ( *s )
{
for ( char *first = s, *last = s + strlen( s ); first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
int main( void )
{
char s1[] = "1";
char s2[] = "12";
char s3[] = "123";
puts( reverse( s1 ) );
puts( reverse( s2 ) );
puts( reverse( s3 ) );
}
The program output is
1
21
321
A simple solution:
char *sl = sf;
while (*sl != 0)
++ sl;
-- sl;
while (sf < sl)
{
char c = *sf;
*sf = *sl;
*sl = c;
++sf, --sl;
}
Find the end of the string by skipping all characters until you find the NUL (zero) character.
Then step back one character (decrement sl) so that you have pointers to the first and the last character of the string.
Then walk both pointers towards one another and swap characters until the pointers meet or cross.
Your code has plenty of issues (bugs 🐛):
char ri = strlen((s) - 1); has to be size_t ri = strlen((s)) - 1;
Other code is very hard to analyze as you use not self explanatory variable names.
Here you have a bit more simple code and much easier to analyse.
char *reverseString(char *str)
{
char *wrk = str, *end;
if(str && *str)
{
end = str + strlen(str) - 1;
while(end > wrk)
{
char temp = *wrk;
*wrk++ = *end;
*end-- = temp;
}
}
return str;
}
int main(void)
{
char str[] = "1234567890";
printf("reversed: %s\n", reverseString(str));
}

C - Freeing string after cycle for

I have this simple function for extrapolate a substring in a string.
char* substr(const char *string, size_t start, size_t end) {
const char *char_start = &string[start];
const char *char_end = &string[end];
char *substring = (char *) calloc(1, char_end - char_start + 1);
memcpy(substring, char_start, char_end - char_start + 1);
return substring;
}
I have only one calloc, that create the returned string.
I try the code in a cycle, for extrapolate the substring of a string array.
This is the main code where I test the function:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t i;
char *tmp = NULL;
char *kmer_array[5] = {"GTGAA", "ACGGT", "AACGG", "AGTGA", "TGAAC"};
for ( i = 0; i < 5; i++ ) {
tmp = substr(kmer_array[i], 1, strlen(kmer_array[i]));
}
free(tmp);
return 0;
}
But when I test the code with valgrind this is the output (link).
I dont't understade where I lost the byte
You set tmp inside the loop 5 times but only free the last one (outside the loop)

How to concatenate 2 strings using malloc and not the library functions

I need to create a function to concatenate 2 strings, in my case they are already given. I will need to concatenate the strings 'hello' and 'world!' to make it into 'helloworld!'. However, I can't use library functions besides strlen(). I also need to use malloc. I understand malloc would create n amounts of bytes for memory, however, how would I make it so that it can return a string array if thats possible.
Here is what I have so far,
#include <stdio.h>
#include <string.h>
int *my_strcat(const char* const str1, const char *const str2)
{
int s1, s2, s3, i = 0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1 + s2 + 1;
a = char *malloc(size_t s3);
for(i = 0; i < s1; i++)
a[i] = str1[i];
for(i = 0; i < s2; i++)
a[i+s1] = str2[i];
a[i]='\0';
return a;
}
int main(void)
{
printf("%s\n",my_strcat("Hello","world!"));
return 0;
}
Thanks to anyone who can help me out.
This problem is imo a bit simpler with pointers:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *mystrcat(char *a, char *b) {
char *p, *q, *rtn;
rtn = q = malloc(strlen(a) + strlen(b) + 1);
for (p = a; (*q = *p) != '\0'; ++p, ++q) {}
for (p = b; (*q = *p) != '\0'; ++p, ++q) {}
return rtn;
}
int main(void) {
char *rtn = mystrcat("Hello ", "world!");
printf("Returned: %s\n", rtn);
free(rtn);
return 0;
}
But you can do the same thing with indices:
char *mystrcat(char *a, char *b) {
char *rtn = malloc(strlen(a) + strlen(b) + 1);
int p, q = 0;
for (p = 0; (rtn[q] = a[p]) != '\0'; ++p, ++q) {}
for (p = 0; (rtn[q] = b[p]) != '\0'; ++p, ++q) {}
return rtn;
}
Here is an alternate fix. First, you forgot #include <stdlib.h> for malloc(). You return a pointer to char from the function my_strcat(), so you need to change the function prototype to reflect this. I also changed the const declarations so that the pointers are not const, only the values that they point to:
char * my_strcat(const char *str1, const char *str2);
Your call to malloc() is incorrectly cast, and there is no reason to do so anyway in C. It also looks like you were trying to cast the argument in malloc() to size_t. You can do so, but you have to surround the type identifier with parentheses:
a = malloc((size_t) s3);
Instead, I have changed the type declaration for s1, s2, s3, i to size_t since all of these variables are used in the context of string lengths and array indices.
The loops were the most significant change, and the reason that I changed the consts in the function prototype. Your loops looked fine, but you can also use pointers for this. You step through the strings by incrementing a pointer, incrementing a counter i, and store the value stored there in the ith location of a. At the end, the index i has been incremented to indicate the location one past the last character, and you store a '\0' there. Note that in your original code, the counter i was not incremented to indicate the location of the null terminator of the concatenated string, because you reset it when you looped through str2. #jpw shows one way of solving this problem.
I changed main() just a little. I declared a pointer to char to receive the return value from the function call. That way you can free() the allocated memory when you are through with it.
Here is the modified code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * my_strcat(const char *str1, const char *str2)
{
size_t s1, s2, s3, i = 0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1+s2+1;
a = malloc(s3);
while(*str1 != '\0') {
a[i] = *str1;
str1++;
i++;
}
while(*str2 != '\0') {
a[i] = *str2;
str2++;
i++;
}
a[i] = '\0'; // Here i = s1 + s2
return a;
}
int main(void)
{
char *str = my_strcat("Hello", "world!");
printf("%s\n", str);
/* Always free allocated memory! */
free(str);
return 0;
}
There are a few issues:
In the return from malloc you don't need to do any cast (you had the syntax for the cast wrong anyway) (see this for more information).
You need to include the header stdlib.h for the malloc function.
And most importantly, a[i]='\0'; in this i is not what you need it to be; you want to add the null char at the end which should be a[s3]='\0'; (the length of s1+s2).
This version should be correct (unless I missed something):
#include <stdio.h>
#include <stdlib.h> //for malloc
#include <string.h>
char *my_strcat(const char* const str1, const char *const str2)
{
int s1,s2,s3,i=0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1+s2+1;
a = malloc(s3);
for(i = 0; i < s1; i++) {
a[i] = str1[i];
}
for(i = 0; i < s2; i++) {
a[i+s1] = str2[i];
}
a[s3-1] = '\0'; // you need the size of s1 + s2 + 1 here, but - 1 as it is 0-indexed
return a;
}
int main(void)
{
printf("%s\n",my_strcat("Hello","world!"));
return 0;
}
Testing with Ideone renders this output: Helloworld!

printf overwriting seeminlgy unrelated data?

EDIT: I should add how I have this all set up. The struct definition and prototypes are in mystring.h. The function definitions are in mystring.c. The main is in mystringtest.c. For mystring.c and mystringtest.c, I have #include "mystring.h" at the top. I'm compiling like gcc -o test.exe mystring.c mystringtest.c. Not sure if any of that matters, but I'm new with C so I'm just trying to include everything.
I have a good deal of experience with Java but am pretty new to C. I imagine this is related to pointers and memory but I'm totally at a loss here for what's going on. Here's my code:
typedef struct {
char *chars;
int length;
int maxSize;
} String;
int main() {
char *a;
a = readline();
String *s = newString(a);
int b = length(s);
printf("length is %d \n", b);
}
I run the program and enter "hello" (as prompted by readline()). I've stepped through the program and after length(s), s->chars is still a pointer to the array of chars 'hello'. After the print statement, s->chars becomes a pointer to the array of chars 'Length is %d \n'. I'm totally at a loss for what I'm doing wrong. I'm working on a virtual machine if that matters at all. Any help is greatly appreciated. I'll give the code for newString and length too.
int length(String *s) {
char *temp = s->chars;
char b = *temp;
int count;
if (b == '\0') { count = 0; }
else { count = 1; }
while (b != '\0') {
b = *(temp+count);
count++;
}
return count;
}
String *newString(char *s) {
String st;
st.length = 20;
st.maxSize = MAXCHAR;
char *temp = malloc(20 * sizeof(char));
char b = *s;
int count = 0;
while (b != '\0') {
*(temp + count) = b;
count++;
b = *(s+count);
if (count == st.maxSize) { break; }
if (count == st.length) {
st.length = st.length + 20;
temp = realloc(temp, st.length * sizeof(char));
}
}
st.chars = temp;
return &st;
}
String *newString(char *s) {
String st;
...
return &st;
}
You are returning a pointer to a local variable. After newString returns, the local variable no longer exists, so you have a dangling pointer.
Either allocate st with malloc, or return it by value.
you must null terminate the string after the while loop, you have not left space for the null terminator. Also I don't see why you need to realloc
//using strlen will eliminate the need for realloc, +1 is for the null terminator
int len = strlen(s)
char *temp = malloc((len * sizeof(char)) +1);
//null terminate
*(temp+count) = '\0';
st.chars = temp;

Resources