Loop returns the current page’s permalink and guid instead of the post in the loop

I’m actually surprised that your code is working. There are several typos in it that should be throwing fatal errors.

global $post;
$args = array(
    'posts_per_page'   => -1,
    'post_type'        => 'post',
);
$the_query = new WP_Query( $args );

if ( $the_query->have_posts() ) :
    while ( $the_query->have_posts() ) : ?>
       <?php $the_query->the_post(); 
       $id = get_the_ID(); ?>
        <div class="row">
            <div class="col-md-6">
                <div class="card">
                    <h4><?php echo get_the_title( $id ); ?></h4>
                    <p><?php echo get_the_excerpt( $id ); ?></p>
                    <?php echo get_the_permalink( $id ); ?>

                    <a href="https://wordpress.stackexchange.com/questions/276549/<?php echo get_the_permalink( $id ); ?>">Read More</a>
                </div>
            </div>
        </div>
    <?php  endwhile;
else :
    echo wpautop( 'Sorry, no posts were found' );
endif;

You mentioned that get_the_ID(); function works fine. So, what you can do is to manually feed the permalink function with the ID, something that I’ve done in my answer.

Also, you have a couple of missing semicolons, which I’ve corrected in the answer.

As a side note, ever use short PHP tags <?. They are deprecated, and on my system they even throw a fatal error. Always use full <?php tags.